diff --git a/Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection.go b/Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection.go index 8ea3ddbc4..1cadd62b2 100644 --- a/Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection.go +++ b/Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection.go @@ -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) } diff --git a/Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection_test.go b/Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection_test.go index 8853bd8a9..876af4e3d 100644 --- a/Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection_test.go @@ -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) diff --git a/Godeps/_workspace/src/github.com/tendermint/go-p2p/switch.go b/Godeps/_workspace/src/github.com/tendermint/go-p2p/switch.go index 0a42997a4..a3ee16c60 100644 --- a/Godeps/_workspace/src/github.com/tendermint/go-p2p/switch.go +++ b/Godeps/_workspace/src/github.com/tendermint/go-p2p/switch.go @@ -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) } } diff --git a/Godeps/_workspace/src/github.com/tendermint/go-p2p/switch_test.go b/Godeps/_workspace/src/github.com/tendermint/go-p2p/switch_test.go index be5951b30..ee08b6f96 100644 --- a/Godeps/_workspace/src/github.com/tendermint/go-p2p/switch_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/go-p2p/switch_test.go @@ -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"}, diff --git a/Makefile b/Makefile index d5a8d7028..a0648ff5b 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/account/account.go b/account/account.go deleted file mode 100644 index cb1983d7f..000000000 --- a/account/account.go +++ /dev/null @@ -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, -} diff --git a/account/priv_account.go b/account/priv_account.go deleted file mode 100644 index b46493fbf..000000000 --- a/account/priv_account.go +++ /dev/null @@ -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, - } -} diff --git a/account/priv_key.go b/account/priv_key.go deleted file mode 100644 index 78d39272d..000000000 --- a/account/priv_key.go +++ /dev/null @@ -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) -} diff --git a/account/pub_key.go b/account/pub_key.go deleted file mode 100644 index 0e7d8fa0d..000000000 --- a/account/pub_key.go +++ /dev/null @@ -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 - } -} diff --git a/account/signature.go b/account/signature.go deleted file mode 100644 index 1224c5c99..000000000 --- a/account/signature.go +++ /dev/null @@ -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[:])) } diff --git a/account/signature_test.go b/account/signature_test.go deleted file mode 100644 index 09d35b910..000000000 --- a/account/signature_test.go +++ /dev/null @@ -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") - } -} diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 1f2a9eb8e..49d692365 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -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) diff --git a/cmd/barak/types/command.go b/cmd/barak/types/command.go index e94de62d8..f764d65ed 100644 --- a/cmd/barak/types/command.go +++ b/cmd/barak/types/command.go @@ -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 { diff --git a/cmd/barak/types/validator.go b/cmd/barak/types/validator.go index 2db48609d..cd099deed 100644 --- a/cmd/barak/types/validator.go +++ b/cmd/barak/types/validator.go @@ -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 } diff --git a/cmd/barak/validate.go b/cmd/barak/validate.go index 839bc3209..a6643b1b4 100644 --- a/cmd/barak/validate.go +++ b/cmd/barak/validate.go @@ -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 { diff --git a/cmd/debora/commands.go b/cmd/debora/commands.go index 793f54e11..ab3f7a70c 100644 --- a/cmd/debora/commands.go +++ b/cmd/debora/commands.go @@ -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, diff --git a/cmd/debora/main.go b/cmd/debora/main.go index b4b836fc3..e4aa0fe30 100644 --- a/cmd/debora/main.go +++ b/cmd/debora/main.go @@ -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() { diff --git a/cmd/sim_txs/README.md b/cmd/sim_txs/README.md deleted file mode 100644 index 1599f7092..000000000 --- a/cmd/sim_txs/README.md +++ /dev/null @@ -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. diff --git a/cmd/sim_txs/main.go b/cmd/sim_txs/main.go deleted file mode 100644 index 10462d412..000000000 --- a/cmd/sim_txs/main.go +++ /dev/null @@ -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 -} diff --git a/cmd/tendermint/gen_account.go b/cmd/tendermint/gen_account.go deleted file mode 100644 index d2dffc525..000000000 --- a/cmd/tendermint/gen_account.go +++ /dev/null @@ -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 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), - ) - } -} diff --git a/cmd/tendermint/get_account.go b/cmd/tendermint/get_account.go deleted file mode 100644 index d2452d16e..000000000 --- a/cmd/tendermint/get_account.go +++ /dev/null @@ -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) -} diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index a266399a3..a4ab2b964 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -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": diff --git a/cmd/tendermint/send_tx.go b/cmd/tendermint/send_tx.go deleted file mode 100644 index 2144826c3..000000000 --- a/cmd/tendermint/send_tx.go +++ /dev/null @@ -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 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.") -} diff --git a/config/tendermint/config.go b/config/tendermint/config.go index df37891a7..2c35f98d1 100644 --- a/config/tendermint/config.go +++ b/config/tendermint/config.go @@ -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 } diff --git a/config/tendermint_test/config.go b/config/tendermint_test/config.go index 1eec76b6d..ca06d731d 100644 --- a/config/tendermint_test/config.go +++ b/config/tendermint_test/config.go @@ -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 } diff --git a/consensus/state.go b/consensus/state.go index 61d0bf47b..7bf80c794 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -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 at (H,R) is called a POL (proof-of-lock). - A POL for 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 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 ) 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 . - * The Prevote step ends: - * After +2/3 prevotes for a particular block or . --> 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 , it unlocks and precommits . - * Else, if +2/3 of prevotes for a particular block or is not received on time, - it precommits . - * The Precommit step ends: - * After +2/3 precommits for a particular block. --> goto Commit(H) - * After +2/3 precommits for . --> 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 ) 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. diff --git a/consensus/state_test.go b/consensus/state_test.go index ff37f9c3a..335ad0291 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -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 } //------------------------------------------------------------------------------------------ diff --git a/consensus/test.go b/consensus/test.go index a05b8ee6e..4a5e9bf04 100644 --- a/consensus/test.go +++ b/consensus/test.go @@ -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) diff --git a/crawler/crawl.go b/crawler/crawl.go deleted file mode 100644 index b4b264335..000000000 --- a/crawler/crawl.go +++ /dev/null @@ -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 -} diff --git a/crawler/log.go b/crawler/log.go deleted file mode 100644 index 14ba849c1..000000000 --- a/crawler/log.go +++ /dev/null @@ -1,7 +0,0 @@ -package crawler - -import ( - "github.com/tendermint/log15" -) - -var log = log15.New("module", "crawler") diff --git a/docs/sts-final.pdf b/docs/sts-final.pdf deleted file mode 100644 index 659f86969..000000000 Binary files a/docs/sts-final.pdf and /dev/null differ diff --git a/mempool/mempool.go b/mempool/mempool.go index 84fa68bd5..66b2e2e44 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -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) } } diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go deleted file mode 100644 index 513617cf4..000000000 --- a/mempool/mempool_test.go +++ /dev/null @@ -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 -} diff --git a/node/id.go b/node/id.go index 2d52213f1..c521aa4af 100644 --- a/node/id.go +++ b/node/id.go @@ -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 { diff --git a/node/node.go b/node/node.go index d0212a9fe..47a358a90 100644 --- a/node/node.go +++ b/node/node.go @@ -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 { diff --git a/node/node_test.go b/node/node_test.go index 38f81d5b1..b94d3b95b 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -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()) diff --git a/permission/types/errors.go b/permission/types/errors.go deleted file mode 100644 index 4b1d7cb94..000000000 --- a/permission/types/errors.go +++ /dev/null @@ -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) -} diff --git a/permission/types/permissions.go b/permission/types/permissions.go deleted file mode 100644 index 3c43ed17b..000000000 --- a/permission/types/permissions.go +++ /dev/null @@ -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 -} diff --git a/permission/types/snatives.go b/permission/types/snatives.go deleted file mode 100644 index 57315ac93..000000000 --- a/permission/types/snatives.go +++ /dev/null @@ -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 -} diff --git a/rpc/client/client.go b/rpc/client/client.go index a19fdedfa..80e15744e 100644 --- a/rpc/client/client.go +++ b/rpc/client/client.go @@ -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) { diff --git a/rpc/core/accounts.go b/rpc/core/accounts.go deleted file mode 100644 index 6f165e765..000000000 --- a/rpc/core/accounts.go +++ /dev/null @@ -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 -} diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index a397499a5..e4bfb8c3c 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -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) { diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 841ced14f..cf9119835 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -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) { diff --git a/rpc/core/names.go b/rpc/core/names.go deleted file mode 100644 index 318beaafc..000000000 --- a/rpc/core/names.go +++ /dev/null @@ -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 -} diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index f17979893..5fbb22d83 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -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 } diff --git a/rpc/core/routes.go b/rpc/core/routes.go index afc9586ae..3deb7ec2c 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -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. } diff --git a/rpc/core/txs.go b/rpc/core/txs.go deleted file mode 100644 index 083a3ad0e..000000000 --- a/rpc/core/txs.go +++ /dev/null @@ -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 -} diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index b9efb5f01..9867842f7 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -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}, ) diff --git a/rpc/core_client/client.go b/rpc/core_client/client.go deleted file mode 100644 index 33c11bba5..000000000 --- a/rpc/core_client/client.go +++ /dev/null @@ -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 -}*/ diff --git a/rpc/core_client/client_methods.go b/rpc/core_client/client_methods.go deleted file mode 100644 index 29c2ac539..000000000 --- a/rpc/core_client/client_methods.go +++ /dev/null @@ -1,1042 +0,0 @@ -// File generated by github.com/ebuchman/rpc-gen - -package core_client - -import ( - "fmt" - acm "github.com/tendermint/tendermint/account" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - rpctypes "github.com/tendermint/tendermint/rpc/types" - "github.com/tendermint/tendermint/types" - "io/ioutil" - "net/http" -) - -type Client interface { - BlockchainInfo(minHeight int, maxHeight int) (*ctypes.ResultBlockchainInfo, error) - BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) - Call(fromAddress []byte, toAddress []byte, data []byte) (*ctypes.ResultCall, error) - CallCode(fromAddress []byte, code []byte, data []byte) (*ctypes.ResultCall, error) - DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) - DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) - GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) - Genesis() (*ctypes.ResultGenesis, error) - GetAccount(address []byte) (*ctypes.ResultGetAccount, error) - GetBlock(height int) (*ctypes.ResultGetBlock, error) - GetName(name string) (*ctypes.ResultGetName, error) - GetStorage(address []byte, key []byte) (*ctypes.ResultGetStorage, error) - ListAccounts() (*ctypes.ResultListAccounts, error) - ListNames() (*ctypes.ResultListNames, error) - ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) - ListValidators() (*ctypes.ResultListValidators, error) - NetInfo() (*ctypes.ResultNetInfo, error) - SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) - Status() (*ctypes.ResultStatus, error) -} - -func (c *ClientHTTP) BlockchainInfo(minHeight int, maxHeight int) (*ctypes.ResultBlockchainInfo, error) { - values, err := argsToURLValues([]string{"minHeight", "maxHeight"}, minHeight, maxHeight) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["BlockchainInfo"], 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.(*ctypes.ResultBlockchainInfo) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { - values, err := argsToURLValues([]string{"tx"}, tx) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["BroadcastTx"], 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.(*ctypes.ResultBroadcastTx) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) Call(fromAddress []byte, toAddress []byte, data []byte) (*ctypes.ResultCall, error) { - values, err := argsToURLValues([]string{"fromAddress", "toAddress", "data"}, fromAddress, toAddress, data) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["Call"], 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.(*ctypes.ResultCall) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) CallCode(fromAddress []byte, code []byte, data []byte) (*ctypes.ResultCall, error) { - values, err := argsToURLValues([]string{"fromAddress", "code", "data"}, fromAddress, code, data) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["CallCode"], 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.(*ctypes.ResultCall) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { - values, err := argsToURLValues(nil) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["DumpConsensusState"], 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.(*ctypes.ResultDumpConsensusState) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) { - values, err := argsToURLValues([]string{"address"}, address) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["DumpStorage"], 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.(*ctypes.ResultDumpStorage) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) { - values, err := argsToURLValues(nil) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["GenPrivAccount"], 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.(*ctypes.ResultGenPrivAccount) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) Genesis() (*ctypes.ResultGenesis, error) { - values, err := argsToURLValues(nil) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["Genesis"], 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.(*ctypes.ResultGenesis) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) GetAccount(address []byte) (*ctypes.ResultGetAccount, error) { - values, err := argsToURLValues([]string{"address"}, address) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["GetAccount"], 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.(*ctypes.ResultGetAccount) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) GetBlock(height int) (*ctypes.ResultGetBlock, error) { - values, err := argsToURLValues([]string{"height"}, height) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["GetBlock"], 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.(*ctypes.ResultGetBlock) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) GetName(name string) (*ctypes.ResultGetName, error) { - values, err := argsToURLValues([]string{"name"}, name) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["GetName"], 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.(*ctypes.ResultGetName) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) GetStorage(address []byte, key []byte) (*ctypes.ResultGetStorage, error) { - values, err := argsToURLValues([]string{"address", "key"}, address, key) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["GetStorage"], 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.(*ctypes.ResultGetStorage) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) ListAccounts() (*ctypes.ResultListAccounts, error) { - values, err := argsToURLValues(nil) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["ListAccounts"], 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.(*ctypes.ResultListAccounts) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) ListNames() (*ctypes.ResultListNames, error) { - values, err := argsToURLValues(nil) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["ListNames"], 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.(*ctypes.ResultListNames) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) { - values, err := argsToURLValues(nil) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["ListUnconfirmedTxs"], 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.(*ctypes.ResultListUnconfirmedTxs) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) ListValidators() (*ctypes.ResultListValidators, error) { - values, err := argsToURLValues(nil) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["ListValidators"], 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.(*ctypes.ResultListValidators) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) NetInfo() (*ctypes.ResultNetInfo, error) { - values, err := argsToURLValues(nil) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["NetInfo"], 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.(*ctypes.ResultNetInfo) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) { - values, err := argsToURLValues([]string{"tx", "privAccounts"}, tx, privAccounts) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["SignTx"], 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.(*ctypes.ResultSignTx) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientHTTP) Status() (*ctypes.ResultStatus, error) { - values, err := argsToURLValues(nil) - if err != nil { - return nil, err - } - resp, err := http.PostForm(c.addr+reverseFuncMap["Status"], 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.(*ctypes.ResultStatus) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) BlockchainInfo(minHeight int, maxHeight int) (*ctypes.ResultBlockchainInfo, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["BlockchainInfo"], - Params: []interface{}{minHeight, maxHeight}, - 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.(*ctypes.ResultBlockchainInfo) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["BroadcastTx"], - Params: []interface{}{tx}, - 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.(*ctypes.ResultBroadcastTx) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) Call(fromAddress []byte, toAddress []byte, data []byte) (*ctypes.ResultCall, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["Call"], - Params: []interface{}{fromAddress, toAddress, data}, - 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.(*ctypes.ResultCall) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) CallCode(fromAddress []byte, code []byte, data []byte) (*ctypes.ResultCall, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["CallCode"], - Params: []interface{}{fromAddress, code, data}, - 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.(*ctypes.ResultCall) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["DumpConsensusState"], - Params: []interface{}{}, - 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.(*ctypes.ResultDumpConsensusState) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["DumpStorage"], - Params: []interface{}{address}, - 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.(*ctypes.ResultDumpStorage) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["GenPrivAccount"], - Params: []interface{}{}, - 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.(*ctypes.ResultGenPrivAccount) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) Genesis() (*ctypes.ResultGenesis, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["Genesis"], - Params: []interface{}{}, - 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.(*ctypes.ResultGenesis) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) GetAccount(address []byte) (*ctypes.ResultGetAccount, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["GetAccount"], - Params: []interface{}{address}, - 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.(*ctypes.ResultGetAccount) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) GetBlock(height int) (*ctypes.ResultGetBlock, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["GetBlock"], - Params: []interface{}{height}, - 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.(*ctypes.ResultGetBlock) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) GetName(name string) (*ctypes.ResultGetName, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["GetName"], - Params: []interface{}{name}, - 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.(*ctypes.ResultGetName) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) GetStorage(address []byte, key []byte) (*ctypes.ResultGetStorage, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["GetStorage"], - Params: []interface{}{address, key}, - 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.(*ctypes.ResultGetStorage) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) ListAccounts() (*ctypes.ResultListAccounts, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["ListAccounts"], - Params: []interface{}{}, - 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.(*ctypes.ResultListAccounts) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) ListNames() (*ctypes.ResultListNames, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["ListNames"], - Params: []interface{}{}, - 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.(*ctypes.ResultListNames) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["ListUnconfirmedTxs"], - Params: []interface{}{}, - 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.(*ctypes.ResultListUnconfirmedTxs) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) ListValidators() (*ctypes.ResultListValidators, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["ListValidators"], - Params: []interface{}{}, - 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.(*ctypes.ResultListValidators) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) NetInfo() (*ctypes.ResultNetInfo, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["NetInfo"], - Params: []interface{}{}, - 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.(*ctypes.ResultNetInfo) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["SignTx"], - Params: []interface{}{tx, privAccounts}, - 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.(*ctypes.ResultSignTx) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} - -func (c *ClientJSON) Status() (*ctypes.ResultStatus, error) { - request := rpctypes.RPCRequest{ - JSONRPC: "2.0", - Method: reverseFuncMap["Status"], - Params: []interface{}{}, - 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.(*ctypes.ResultStatus) - if !ok { - return nil, fmt.Errorf("response result was wrong type") - } - return result, nil -} diff --git a/rpc/core_client/log.go b/rpc/core_client/log.go deleted file mode 100644 index b0fffb7af..000000000 --- a/rpc/core_client/log.go +++ /dev/null @@ -1,8 +0,0 @@ - -package core_client - -import ( - "github.com/tendermint/log15" -) - -var log = log15.New("module", "core_client") diff --git a/rpc/core_client/ws_client.go b/rpc/core_client/ws_client.go deleted file mode 100644 index 9778477e9..000000000 --- a/rpc/core_client/ws_client.go +++ /dev/null @@ -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 -} diff --git a/rpc/test/client_rpc_test.go b/rpc/test/client_rpc_test.go deleted file mode 100644 index 4b5e6d1e3..000000000 --- a/rpc/test/client_rpc_test.go +++ /dev/null @@ -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") -} diff --git a/rpc/test/client_ws_test.go b/rpc/test/client_ws_test.go deleted file mode 100644 index 991764848..000000000 --- a/rpc/test/client_ws_test.go +++ /dev/null @@ -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)) -} diff --git a/rpc/test/config.go b/rpc/test/config.go deleted file mode 100644 index 2fe20981c..000000000 --- a/rpc/test/config.go +++ /dev/null @@ -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 - }) -} diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go deleted file mode 100644 index 5b6da90bf..000000000 --- a/rpc/test/helpers.go +++ /dev/null @@ -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) -} diff --git a/rpc/test/tests.go b/rpc/test/tests.go deleted file mode 100644 index 027e4502b..000000000 --- a/rpc/test/tests.go +++ /dev/null @@ -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)) - } -} diff --git a/rpc/test/ws_helpers.go b/rpc/test/ws_helpers.go deleted file mode 100644 index 0ca30207a..000000000 --- a/rpc/test/ws_helpers.go +++ /dev/null @@ -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 - } -} diff --git a/state/block_cache.go b/state/block_cache.go deleted file mode 100644 index 6f0fb3565..000000000 --- a/state/block_cache.go +++ /dev/null @@ -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 -} diff --git a/state/common.go b/state/common.go deleted file mode 100644 index 87b8497a5..000000000 --- a/state/common.go +++ /dev/null @@ -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 -} diff --git a/state/execution.go b/state/execution.go index a8a04e3d3..9c43e1c9a 100644 --- a/state/execution.go +++ b/state/execution.go @@ -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 } //----------------------------------------------------------------------------- diff --git a/state/genesis_test.go b/state/genesis_test.go deleted file mode 100644 index 251f04df0..000000000 --- a/state/genesis_test.go +++ /dev/null @@ -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) - } -} diff --git a/state/permissions_test.go b/state/permissions_test.go deleted file mode 100644 index 4a9bad09a..000000000 --- a/state/permissions_test.go +++ /dev/null @@ -1,1265 +0,0 @@ -package state - -import ( - "bytes" - "fmt" - "strconv" - "testing" - "time" - - acm "github.com/tendermint/tendermint/account" - . "github.com/tendermint/go-common" - dbm "github.com/tendermint/go-db" - "github.com/tendermint/tendermint/events" - ptypes "github.com/tendermint/tendermint/permission/types" - . "github.com/tendermint/tendermint/state/types" - "github.com/tendermint/tendermint/types" -) - -/* -Permission Tests: - -- SendTx: -x - 1 input, no perm, call perm, create perm -x - 1 input, perm -x - 2 inputs, one with perm one without - -- CallTx, CALL -x - 1 input, no perm, send perm, create perm -x - 1 input, perm -x - contract runs call but doesn't have call perm -x - contract runs call and has call perm -x - contract runs call (with perm), runs contract that runs call (without perm) -x - contract runs call (with perm), runs contract that runs call (with perm) - -- CallTx for Create, CREATE -x - 1 input, no perm, send perm, call perm -x - 1 input, perm -x - contract runs create but doesn't have create perm -x - contract runs create but has perm -x - contract runs call with empty address (has call and create perm) - -- NameTx - - no perm, send perm, call perm - - with perm - -- BondTx -x - 1 input, no perm -x - 1 input, perm -x - 1 bonder with perm, input without send or bond -x - 1 bonder with perm, input with send -x - 1 bonder with perm, input with bond -x - 2 inputs, one with perm one without - -- SendTx for new account -x - 1 input, 1 unknown ouput, input with send, not create (fail) -x - 1 input, 1 unknown ouput, input with send and create (pass) -x - 2 inputs, 1 unknown ouput, both inputs with send, one with create, one without (fail) -x - 2 inputs, 1 known output, 1 unknown ouput, one input with create, one without (fail) -x - 2 inputs, 1 unknown ouput, both inputs with send, both inputs with create (pass ) -x - 2 inputs, 1 known output, 1 unknown ouput, both inputs with create, (pass) - - -- CALL for new account -x - unknown output, without create (fail) -x - unknown output, with create (pass) - - -- SNative (CallTx, CALL): - - for each of CallTx, Call -x - call each snative without permission, fails -x - call each snative with permission, pass - - list: -x - base: has,set,unset -x - globals: set -x - roles: has, add, rm - - -*/ - -// keys -var user = makeUsers(10) -var chainID = "testchain" - -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 -} - -var ( - PermsAllFalse = ptypes.ZeroAccountPermissions -) - -func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) GenesisDoc { - genAccounts := []GenesisAccount{} - for _, u := range user[:5] { - accountPermCopy := accountPerm // Create new instance for custom overridability. - genAccounts = append(genAccounts, GenesisAccount{ - Address: u.Address, - Amount: 1000000, - Permissions: &accountPermCopy, - }) - } - - return GenesisDoc{ - GenesisTime: time.Now(), - ChainID: chainID, - Params: &GenesisParams{ - GlobalPermissions: &globalPerm, - }, - Accounts: genAccounts, - Validators: []GenesisValidator{ - GenesisValidator{ - PubKey: user[0].PubKey.(acm.PubKeyEd25519), - Amount: 10, - UnbondTo: []BasicAccount{ - BasicAccount{ - Address: user[0].Address, - }, - }, - }, - }, - } -} - -func TestSendFails(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[1].Permissions.Base.Set(ptypes.Send, true) - genDoc.Accounts[2].Permissions.Base.Set(ptypes.Call, true) - genDoc.Accounts[3].Permissions.Base.Set(ptypes.CreateContract, true) - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - - //------------------- - // send txs - - // simple send tx should fail - tx := types.NewSendTx() - if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[1].Address, 5) - tx.SignInput(chainID, 0, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // simple send tx with call perm should fail - tx = types.NewSendTx() - if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[4].Address, 5) - tx.SignInput(chainID, 0, user[2]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // simple send tx with create perm should fail - tx = types.NewSendTx() - if err := tx.AddInput(blockCache, user[3].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[4].Address, 5) - tx.SignInput(chainID, 0, user[3]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // simple send tx to unknown account without create_account perm should fail - acc := blockCache.GetAccount(user[3].Address) - acc.Permissions.Base.Set(ptypes.Send, true) - blockCache.UpdateAccount(acc) - tx = types.NewSendTx() - if err := tx.AddInput(blockCache, user[3].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[6].Address, 5) - tx.SignInput(chainID, 0, user[3]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } -} - -func TestName(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[0].Permissions.Base.Set(ptypes.Send, true) - genDoc.Accounts[1].Permissions.Base.Set(ptypes.Name, true) - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - - //------------------- - // name txs - - // simple name tx without perm should fail - tx, err := types.NewNameTx(st, user[0].PubKey, "somename", "somedata", 10000, 100) - if err != nil { - t.Fatal(err) - } - tx.Sign(chainID, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // simple name tx with perm should pass - tx, err = types.NewNameTx(st, user[1].PubKey, "somename", "somedata", 10000, 100) - if err != nil { - t.Fatal(err) - } - tx.Sign(chainID, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal(err) - } -} - -func TestCallFails(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[1].Permissions.Base.Set(ptypes.Send, true) - genDoc.Accounts[2].Permissions.Base.Set(ptypes.Call, true) - genDoc.Accounts[3].Permissions.Base.Set(ptypes.CreateContract, true) - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - - //------------------- - // call txs - - // simple call tx should fail - tx, _ := types.NewCallTx(blockCache, user[0].PubKey, user[4].Address, nil, 100, 100, 100) - tx.Sign(chainID, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // simple call tx with send permission should fail - tx, _ = types.NewCallTx(blockCache, user[1].PubKey, user[4].Address, nil, 100, 100, 100) - tx.Sign(chainID, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // simple call tx with create permission should fail - tx, _ = types.NewCallTx(blockCache, user[3].PubKey, user[4].Address, nil, 100, 100, 100) - tx.Sign(chainID, user[3]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - //------------------- - // create txs - - // simple call create tx should fail - tx, _ = types.NewCallTx(blockCache, user[0].PubKey, nil, nil, 100, 100, 100) - tx.Sign(chainID, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // simple call create tx with send perm should fail - tx, _ = types.NewCallTx(blockCache, user[1].PubKey, nil, nil, 100, 100, 100) - tx.Sign(chainID, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // simple call create tx with call perm should fail - tx, _ = types.NewCallTx(blockCache, user[2].PubKey, nil, nil, 100, 100, 100) - tx.Sign(chainID, user[2]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } -} - -func TestSendPermission(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[0].Permissions.Base.Set(ptypes.Send, true) // give the 0 account permission - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - - // A single input, having the permission, should succeed - tx := types.NewSendTx() - if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[1].Address, 5) - tx.SignInput(chainID, 0, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Transaction failed", err) - } - - // Two inputs, one with permission, one without, should fail - tx = types.NewSendTx() - if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil { - t.Fatal(err) - } - if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[2].Address, 10) - tx.SignInput(chainID, 0, user[0]) - tx.SignInput(chainID, 1, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } -} - -func TestCallPermission(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[0].Permissions.Base.Set(ptypes.Call, true) // give the 0 account permission - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - - //------------------------------ - // call to simple contract - fmt.Println("\n##### SIMPLE CONTRACT") - - // create simple contract - simpleContractAddr := NewContractAddress(user[0].Address, 100) - simpleAcc := &acm.Account{ - Address: simpleContractAddr, - Balance: 0, - Code: []byte{0x60}, - Sequence: 0, - StorageRoot: Zero256.Bytes(), - Permissions: ptypes.ZeroAccountPermissions, - } - st.UpdateAccount(simpleAcc) - - // A single input, having the permission, should succeed - tx, _ := types.NewCallTx(blockCache, user[0].PubKey, simpleContractAddr, nil, 100, 100, 100) - tx.Sign(chainID, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Transaction failed", err) - } - - //---------------------------------------------------------- - // call to contract that calls simple contract - without perm - fmt.Println("\n##### CALL TO SIMPLE CONTRACT (FAIL)") - - // create contract that calls the simple contract - contractCode := callContractCode(simpleContractAddr) - caller1ContractAddr := NewContractAddress(user[0].Address, 101) - caller1Acc := &acm.Account{ - Address: caller1ContractAddr, - Balance: 10000, - Code: contractCode, - Sequence: 0, - StorageRoot: Zero256.Bytes(), - Permissions: ptypes.ZeroAccountPermissions, - } - blockCache.UpdateAccount(caller1Acc) - - // A single input, having the permission, but the contract doesn't have permission - tx, _ = types.NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) - tx.Sign(chainID, user[0]) - - // we need to subscribe to the Call event to detect the exception - _, exception := execTxWaitEvent(t, blockCache, tx, types.EventStringAccCall(caller1ContractAddr)) // - if exception == "" { - t.Fatal("Expected exception") - } - - //---------------------------------------------------------- - // call to contract that calls simple contract - with perm - fmt.Println("\n##### CALL TO SIMPLE CONTRACT (PASS)") - - // A single input, having the permission, and the contract has permission - caller1Acc.Permissions.Base.Set(ptypes.Call, true) - blockCache.UpdateAccount(caller1Acc) - tx, _ = types.NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) - tx.Sign(chainID, user[0]) - - // we need to subscribe to the Call event to detect the exception - _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccCall(caller1ContractAddr)) // - if exception != "" { - t.Fatal("Unexpected exception:", exception) - } - - //---------------------------------------------------------- - // call to contract that calls contract that calls simple contract - without perm - // caller1Contract calls simpleContract. caller2Contract calls caller1Contract. - // caller1Contract does not have call perms, but caller2Contract does. - fmt.Println("\n##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (FAIL)") - - contractCode2 := callContractCode(caller1ContractAddr) - caller2ContractAddr := NewContractAddress(user[0].Address, 102) - caller2Acc := &acm.Account{ - Address: caller2ContractAddr, - Balance: 1000, - Code: contractCode2, - Sequence: 0, - StorageRoot: Zero256.Bytes(), - Permissions: ptypes.ZeroAccountPermissions, - } - caller1Acc.Permissions.Base.Set(ptypes.Call, false) - caller2Acc.Permissions.Base.Set(ptypes.Call, true) - blockCache.UpdateAccount(caller1Acc) - blockCache.UpdateAccount(caller2Acc) - - tx, _ = types.NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) - tx.Sign(chainID, user[0]) - - // we need to subscribe to the Call event to detect the exception - _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccCall(caller1ContractAddr)) // - if exception == "" { - t.Fatal("Expected exception") - } - - //---------------------------------------------------------- - // call to contract that calls contract that calls simple contract - without perm - // caller1Contract calls simpleContract. caller2Contract calls caller1Contract. - // both caller1 and caller2 have permission - fmt.Println("\n##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (PASS)") - - caller1Acc.Permissions.Base.Set(ptypes.Call, true) - blockCache.UpdateAccount(caller1Acc) - - tx, _ = types.NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) - tx.Sign(chainID, user[0]) - - // we need to subscribe to the Call event to detect the exception - _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccCall(caller1ContractAddr)) // - if exception != "" { - t.Fatal("Unexpected exception", exception) - } -} - -func TestCreatePermission(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[0].Permissions.Base.Set(ptypes.CreateContract, true) // give the 0 account permission - genDoc.Accounts[0].Permissions.Base.Set(ptypes.Call, true) // give the 0 account permission - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - - //------------------------------ - // create a simple contract - fmt.Println("\n##### CREATE SIMPLE CONTRACT") - - contractCode := []byte{0x60} - createCode := wrapContractForCreate(contractCode) - - // A single input, having the permission, should succeed - tx, _ := types.NewCallTx(blockCache, user[0].PubKey, nil, createCode, 100, 100, 100) - tx.Sign(chainID, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Transaction failed", err) - } - // ensure the contract is there - contractAddr := NewContractAddress(tx.Input.Address, tx.Input.Sequence) - contractAcc := blockCache.GetAccount(contractAddr) - if contractAcc == nil { - t.Fatalf("failed to create contract %X", contractAddr) - } - if bytes.Compare(contractAcc.Code, contractCode) != 0 { - t.Fatalf("contract does not have correct code. Got %X, expected %X", contractAcc.Code, contractCode) - } - - //------------------------------ - // create contract that uses the CREATE op - fmt.Println("\n##### CREATE FACTORY") - - contractCode = []byte{0x60} - createCode = wrapContractForCreate(contractCode) - factoryCode := createContractCode() - createFactoryCode := wrapContractForCreate(factoryCode) - - // A single input, having the permission, should succeed - tx, _ = types.NewCallTx(blockCache, user[0].PubKey, nil, createFactoryCode, 100, 100, 100) - tx.Sign(chainID, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Transaction failed", err) - } - // ensure the contract is there - contractAddr = NewContractAddress(tx.Input.Address, tx.Input.Sequence) - contractAcc = blockCache.GetAccount(contractAddr) - if contractAcc == nil { - t.Fatalf("failed to create contract %X", contractAddr) - } - if bytes.Compare(contractAcc.Code, factoryCode) != 0 { - t.Fatalf("contract does not have correct code. Got %X, expected %X", contractAcc.Code, factoryCode) - } - - //------------------------------ - // call the contract (should FAIL) - fmt.Println("\n###### CALL THE FACTORY (FAIL)") - - // A single input, having the permission, should succeed - tx, _ = types.NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 100, 100) - tx.Sign(chainID, user[0]) - // we need to subscribe to the Call event to detect the exception - _, exception := execTxWaitEvent(t, blockCache, tx, types.EventStringAccCall(contractAddr)) // - if exception == "" { - t.Fatal("expected exception") - } - - //------------------------------ - // call the contract (should PASS) - fmt.Println("\n###### CALL THE FACTORY (PASS)") - - contractAcc.Permissions.Base.Set(ptypes.CreateContract, true) - blockCache.UpdateAccount(contractAcc) - - // A single input, having the permission, should succeed - tx, _ = types.NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 100, 100) - tx.Sign(chainID, user[0]) - // we need to subscribe to the Call event to detect the exception - _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccCall(contractAddr)) // - if exception != "" { - t.Fatal("unexpected exception", exception) - } - - //-------------------------------- - fmt.Println("\n##### CALL to empty address") - zeroAddr := LeftPadBytes([]byte{}, 20) - code := callContractCode(zeroAddr) - - contractAddr = NewContractAddress(user[0].Address, 110) - contractAcc = &acm.Account{ - Address: contractAddr, - Balance: 1000, - Code: code, - Sequence: 0, - StorageRoot: Zero256.Bytes(), - Permissions: ptypes.ZeroAccountPermissions, - } - contractAcc.Permissions.Base.Set(ptypes.Call, true) - contractAcc.Permissions.Base.Set(ptypes.CreateContract, true) - blockCache.UpdateAccount(contractAcc) - - // this should call the 0 address but not create ... - tx, _ = types.NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 10000, 100) - tx.Sign(chainID, user[0]) - // we need to subscribe to the Call event to detect the exception - _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccCall(zeroAddr)) // - if exception != "" { - t.Fatal("unexpected exception", exception) - } - zeroAcc := blockCache.GetAccount(zeroAddr) - if len(zeroAcc.Code) != 0 { - t.Fatal("the zero account was given code from a CALL!") - } -} - -func TestBondPermission(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - var bondAcc *acm.Account - - //------------------------------ - // one bonder without permission should fail - tx, _ := types.NewBondTx(user[1].PubKey) - if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[1].Address, 5) - tx.SignInput(chainID, 0, user[1]) - tx.SignBond(chainID, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - //------------------------------ - // one bonder with permission should pass - bondAcc = blockCache.GetAccount(user[1].Address) - bondAcc.Permissions.Base.Set(ptypes.Bond, true) - blockCache.UpdateAccount(bondAcc) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Unexpected error", err) - } - - // reset state (we can only bond with an account once ..) - genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st = MakeGenesisState(stateDB, &genDoc) - blockCache = NewBlockCache(st) - bondAcc = blockCache.GetAccount(user[1].Address) - bondAcc.Permissions.Base.Set(ptypes.Bond, true) - blockCache.UpdateAccount(bondAcc) - //------------------------------ - // one bonder with permission and an input without send should fail - tx, _ = types.NewBondTx(user[1].PubKey) - if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[1].Address, 5) - tx.SignInput(chainID, 0, user[2]) - tx.SignBond(chainID, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // reset state (we can only bond with an account once ..) - genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st = MakeGenesisState(stateDB, &genDoc) - blockCache = NewBlockCache(st) - bondAcc = blockCache.GetAccount(user[1].Address) - bondAcc.Permissions.Base.Set(ptypes.Bond, true) - blockCache.UpdateAccount(bondAcc) - //------------------------------ - // one bonder with permission and an input with send should pass - sendAcc := blockCache.GetAccount(user[2].Address) - sendAcc.Permissions.Base.Set(ptypes.Send, true) - blockCache.UpdateAccount(sendAcc) - tx, _ = types.NewBondTx(user[1].PubKey) - if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[1].Address, 5) - tx.SignInput(chainID, 0, user[2]) - tx.SignBond(chainID, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Unexpected error", err) - } - - // reset state (we can only bond with an account once ..) - genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st = MakeGenesisState(stateDB, &genDoc) - blockCache = NewBlockCache(st) - bondAcc = blockCache.GetAccount(user[1].Address) - bondAcc.Permissions.Base.Set(ptypes.Bond, true) - blockCache.UpdateAccount(bondAcc) - //------------------------------ - // one bonder with permission and an input with bond should pass - sendAcc.Permissions.Base.Set(ptypes.Bond, true) - blockCache.UpdateAccount(sendAcc) - tx, _ = types.NewBondTx(user[1].PubKey) - if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[1].Address, 5) - tx.SignInput(chainID, 0, user[2]) - tx.SignBond(chainID, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Unexpected error", err) - } - - // reset state (we can only bond with an account once ..) - genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st = MakeGenesisState(stateDB, &genDoc) - blockCache = NewBlockCache(st) - bondAcc = blockCache.GetAccount(user[1].Address) - bondAcc.Permissions.Base.Set(ptypes.Bond, true) - blockCache.UpdateAccount(bondAcc) - //------------------------------ - // one bonder with permission and an input from that bonder and an input without send or bond should fail - tx, _ = types.NewBondTx(user[1].PubKey) - if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil { - t.Fatal(err) - } - if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[1].Address, 5) - tx.SignInput(chainID, 0, user[1]) - tx.SignInput(chainID, 1, user[2]) - tx.SignBond(chainID, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } -} - -func TestCreateAccountPermission(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[0].Permissions.Base.Set(ptypes.Send, true) // give the 0 account permission - genDoc.Accounts[1].Permissions.Base.Set(ptypes.Send, true) // give the 0 account permission - genDoc.Accounts[0].Permissions.Base.Set(ptypes.CreateAccount, true) // give the 0 account permission - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - - //---------------------------------------------------------- - // SendTx to unknown account - - // A single input, having the permission, should succeed - tx := types.NewSendTx() - if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[6].Address, 5) - tx.SignInput(chainID, 0, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Transaction failed", err) - } - - // Two inputs, both with send, one with create, one without, should fail - tx = types.NewSendTx() - if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil { - t.Fatal(err) - } - if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[7].Address, 10) - tx.SignInput(chainID, 0, user[0]) - tx.SignInput(chainID, 1, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // Two inputs, both with send, one with create, one without, two ouputs (one known, one unknown) should fail - tx = types.NewSendTx() - if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil { - t.Fatal(err) - } - if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[7].Address, 4) - tx.AddOutput(user[4].Address, 6) - tx.SignInput(chainID, 0, user[0]) - tx.SignInput(chainID, 1, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // Two inputs, both with send, both with create, should pass - acc := blockCache.GetAccount(user[1].Address) - acc.Permissions.Base.Set(ptypes.CreateAccount, true) - blockCache.UpdateAccount(acc) - tx = types.NewSendTx() - if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil { - t.Fatal(err) - } - if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[7].Address, 10) - tx.SignInput(chainID, 0, user[0]) - tx.SignInput(chainID, 1, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Unexpected error", err) - } - - // Two inputs, both with send, both with create, two outputs (one known, one unknown) should pass - tx = types.NewSendTx() - if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil { - t.Fatal(err) - } - if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(user[7].Address, 7) - tx.AddOutput(user[4].Address, 3) - tx.SignInput(chainID, 0, user[0]) - tx.SignInput(chainID, 1, user[1]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Unexpected error", err) - } - - //---------------------------------------------------------- - // CALL to unknown account - - acc = blockCache.GetAccount(user[0].Address) - acc.Permissions.Base.Set(ptypes.Call, true) - blockCache.UpdateAccount(acc) - - // call to contract that calls unknown account - without create_account perm - // create contract that calls the simple contract - contractCode := callContractCode(user[9].Address) - caller1ContractAddr := NewContractAddress(user[4].Address, 101) - caller1Acc := &acm.Account{ - Address: caller1ContractAddr, - Balance: 0, - Code: contractCode, - Sequence: 0, - StorageRoot: Zero256.Bytes(), - Permissions: ptypes.ZeroAccountPermissions, - } - blockCache.UpdateAccount(caller1Acc) - - // A single input, having the permission, but the contract doesn't have permission - txCall, _ := types.NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) - txCall.Sign(chainID, user[0]) - - // we need to subscribe to the Call event to detect the exception - _, exception := execTxWaitEvent(t, blockCache, txCall, types.EventStringAccCall(caller1ContractAddr)) // - if exception == "" { - t.Fatal("Expected exception") - } - - // NOTE: for a contract to be able to CreateAccount, it must be able to call - // NOTE: for a user to be able to CreateAccount, it must be able to send! - caller1Acc.Permissions.Base.Set(ptypes.CreateAccount, true) - caller1Acc.Permissions.Base.Set(ptypes.Call, true) - blockCache.UpdateAccount(caller1Acc) - // A single input, having the permission, but the contract doesn't have permission - txCall, _ = types.NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) - txCall.Sign(chainID, user[0]) - - // we need to subscribe to the Call event to detect the exception - _, exception = execTxWaitEvent(t, blockCache, txCall, types.EventStringAccCall(caller1ContractAddr)) // - if exception != "" { - t.Fatal("Unexpected exception", exception) - } - -} - -// holla at my boy -var DougAddress = append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, []byte("THISISDOUG")...) - -func TestSNativeCALL(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[0].Permissions.Base.Set(ptypes.Call, true) // give the 0 account permission - genDoc.Accounts[3].Permissions.Base.Set(ptypes.Bond, true) // some arbitrary permission to play with - genDoc.Accounts[3].Permissions.AddRole("bumble") - genDoc.Accounts[3].Permissions.AddRole("bee") - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - - //---------------------------------------------------------- - // Test CALL to SNative contracts - - // make the main contract once - doug := &acm.Account{ - Address: DougAddress, - Balance: 0, - Code: nil, - Sequence: 0, - StorageRoot: Zero256.Bytes(), - Permissions: ptypes.ZeroAccountPermissions, - } - doug.Permissions.Base.Set(ptypes.Call, true) - //doug.Permissions.Base.Set(ptypes.HasBase, true) - blockCache.UpdateAccount(doug) - - fmt.Println("\n#### HasBase") - // HasBase - snativeAddress, data := snativePermTestInputCALL("has_base", user[3], ptypes.Bond, false) - testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { - // return value should be true or false as a 32 byte array... - if !IsZeros(ret[:31]) || ret[31] != byte(1) { - return fmt.Errorf("Expected 1. Got %X", ret) - } - return nil - }) - - fmt.Println("\n#### SetBase") - // SetBase - snativeAddress, data = snativePermTestInputCALL("set_base", user[3], ptypes.Bond, false) - testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, data = snativePermTestInputCALL("has_base", user[3], ptypes.Bond, false) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { - // return value should be true or false as a 32 byte array... - if !IsZeros(ret) { - return fmt.Errorf("Expected 0. Got %X", ret) - } - return nil - }) - snativeAddress, data = snativePermTestInputCALL("set_base", user[3], ptypes.CreateContract, true) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, data = snativePermTestInputCALL("has_base", user[3], ptypes.CreateContract, false) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { - // return value should be true or false as a 32 byte array... - if !IsZeros(ret[:31]) || ret[31] != byte(1) { - return fmt.Errorf("Expected 1. Got %X", ret) - } - return nil - }) - - fmt.Println("\n#### UnsetBase") - // UnsetBase - snativeAddress, data = snativePermTestInputCALL("unset_base", user[3], ptypes.CreateContract, false) - testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, data = snativePermTestInputCALL("has_base", user[3], ptypes.CreateContract, false) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { - if !IsZeros(ret) { - return fmt.Errorf("Expected 0. Got %X", ret) - } - return nil - }) - - fmt.Println("\n#### SetGlobal") - // SetGlobalPerm - snativeAddress, data = snativePermTestInputCALL("set_global", user[3], ptypes.CreateContract, true) - testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, data = snativePermTestInputCALL("has_base", user[3], ptypes.CreateContract, false) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { - // return value should be true or false as a 32 byte array... - if !IsZeros(ret[:31]) || ret[31] != byte(1) { - return fmt.Errorf("Expected 1. Got %X", ret) - } - return nil - }) - - fmt.Println("\n#### HasRole") - // HasRole - snativeAddress, data = snativeRoleTestInputCALL("has_role", user[3], "bumble") - testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { - if !IsZeros(ret[:31]) || ret[31] != byte(1) { - return fmt.Errorf("Expected 1. Got %X", ret) - } - return nil - }) - - fmt.Println("\n#### AddRole") - // AddRole - snativeAddress, data = snativeRoleTestInputCALL("has_role", user[3], "chuck") - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { - if !IsZeros(ret) { - return fmt.Errorf("Expected 0. Got %X", ret) - } - return nil - }) - snativeAddress, data = snativeRoleTestInputCALL("add_role", user[3], "chuck") - testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, data = snativeRoleTestInputCALL("has_role", user[3], "chuck") - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { - if !IsZeros(ret[:31]) || ret[31] != byte(1) { - return fmt.Errorf("Expected 1. Got %X", ret) - } - return nil - }) - - fmt.Println("\n#### RmRole") - // RmRole - snativeAddress, data = snativeRoleTestInputCALL("rm_role", user[3], "chuck") - testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, data = snativeRoleTestInputCALL("has_role", user[3], "chuck") - testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { - if !IsZeros(ret) { - return fmt.Errorf("Expected 0. Got %X", ret) - } - return nil - }) -} - -func TestSNativeTx(t *testing.T) { - stateDB := dbm.GetDB("state") - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[0].Permissions.Base.Set(ptypes.Call, true) // give the 0 account permission - genDoc.Accounts[3].Permissions.Base.Set(ptypes.Bond, true) // some arbitrary permission to play with - genDoc.Accounts[3].Permissions.AddRole("bumble") - genDoc.Accounts[3].Permissions.AddRole("bee") - st := MakeGenesisState(stateDB, &genDoc) - blockCache := NewBlockCache(st) - - //---------------------------------------------------------- - // Test SNativeTx - - fmt.Println("\n#### SetBase") - // SetBase - snativeArgs := snativePermTestInputTx("set_base", user[3], ptypes.Bond, false) - testSNativeTxExpectFail(t, blockCache, snativeArgs) - testSNativeTxExpectPass(t, blockCache, ptypes.SetBase, snativeArgs) - acc := blockCache.GetAccount(user[3].Address) - if v, _ := acc.Permissions.Base.Get(ptypes.Bond); v { - t.Fatal("expected permission to be set false") - } - snativeArgs = snativePermTestInputTx("set_base", user[3], ptypes.CreateContract, true) - testSNativeTxExpectPass(t, blockCache, ptypes.SetBase, snativeArgs) - acc = blockCache.GetAccount(user[3].Address) - if v, _ := acc.Permissions.Base.Get(ptypes.CreateContract); !v { - t.Fatal("expected permission to be set true") - } - - fmt.Println("\n#### UnsetBase") - // UnsetBase - snativeArgs = snativePermTestInputTx("unset_base", user[3], ptypes.CreateContract, false) - testSNativeTxExpectFail(t, blockCache, snativeArgs) - testSNativeTxExpectPass(t, blockCache, ptypes.UnsetBase, snativeArgs) - acc = blockCache.GetAccount(user[3].Address) - if v, _ := acc.Permissions.Base.Get(ptypes.CreateContract); v { - t.Fatal("expected permission to be set false") - } - - fmt.Println("\n#### SetGlobal") - // SetGlobalPerm - snativeArgs = snativePermTestInputTx("set_global", user[3], ptypes.CreateContract, true) - testSNativeTxExpectFail(t, blockCache, snativeArgs) - testSNativeTxExpectPass(t, blockCache, ptypes.SetGlobal, snativeArgs) - acc = blockCache.GetAccount(ptypes.GlobalPermissionsAddress) - if v, _ := acc.Permissions.Base.Get(ptypes.CreateContract); !v { - t.Fatal("expected permission to be set true") - } - - fmt.Println("\n#### AddRole") - // AddRole - snativeArgs = snativeRoleTestInputTx("add_role", user[3], "chuck") - testSNativeTxExpectFail(t, blockCache, snativeArgs) - testSNativeTxExpectPass(t, blockCache, ptypes.AddRole, snativeArgs) - acc = blockCache.GetAccount(user[3].Address) - if v := acc.Permissions.HasRole("chuck"); !v { - t.Fatal("expected role to be added") - } - - fmt.Println("\n#### RmRole") - // RmRole - snativeArgs = snativeRoleTestInputTx("rm_role", user[3], "chuck") - testSNativeTxExpectFail(t, blockCache, snativeArgs) - testSNativeTxExpectPass(t, blockCache, ptypes.RmRole, snativeArgs) - acc = blockCache.GetAccount(user[3].Address) - if v := acc.Permissions.HasRole("chuck"); v { - t.Fatal("expected role to be removed") - } -} - -//------------------------------------------------------------------------------------- -// helpers - -var ExceptionTimeOut = "timed out waiting for event" - -// run ExecTx and wait for the Call event on given addr -// returns the msg data and an error/exception -func execTxWaitEvent(t *testing.T, blockCache *BlockCache, tx types.Tx, eventid string) (interface{}, string) { - evsw := events.NewEventSwitch() - evsw.Start() - ch := make(chan interface{}) - evsw.AddListenerForEvent("test", eventid, func(msg types.EventData) { - ch <- msg - }) - evc := events.NewEventCache(evsw) - go func() { - if err := ExecTx(blockCache, tx, true, evc); err != nil { - ch <- err.Error() - } - evc.Flush() - }() - ticker := time.NewTicker(5 * time.Second) - var msg interface{} - select { - case msg = <-ch: - case <-ticker.C: - return nil, ExceptionTimeOut - } - - switch ev := msg.(type) { - case types.EventDataTx: - return ev, ev.Exception - case types.EventDataCall: - return ev, ev.Exception - case string: - return nil, ev - default: - return ev, "" - } -} - -// give a contract perms for an snative, call it, it calls the snative, but shouldn't have permission -func testSNativeCALLExpectFail(t *testing.T, blockCache *BlockCache, doug *acm.Account, snativeAddress, data []byte) { - testSNativeCALL(t, false, blockCache, doug, snativeAddress, data, nil) -} - -// give a contract perms for an snative, call it, it calls the snative, ensure the check funciton (f) succeeds -func testSNativeCALLExpectPass(t *testing.T, blockCache *BlockCache, doug *acm.Account, snativeAddress, data []byte, f func([]byte) error) { - testSNativeCALL(t, true, blockCache, doug, snativeAddress, data, f) -} - -func testSNativeCALL(t *testing.T, expectPass bool, blockCache *BlockCache, doug *acm.Account, snativeAddress, data []byte, f func([]byte) error) { - if expectPass { - perm, err := ptypes.PermStringToFlag(TrimmedString(snativeAddress)) - if err != nil { - t.Fatal(err) - } - doug.Permissions.Base.Set(perm, true) - } - var addr []byte - contractCode := callContractCode(snativeAddress) - doug.Code = contractCode - blockCache.UpdateAccount(doug) - addr = doug.Address - tx, _ := types.NewCallTx(blockCache, user[0].PubKey, addr, data, 100, 10000, 100) - tx.Sign(chainID, user[0]) - fmt.Println("subscribing to", types.EventStringAccCall(snativeAddress)) - ev, exception := execTxWaitEvent(t, blockCache, tx, types.EventStringAccCall(snativeAddress)) - if exception == ExceptionTimeOut { - t.Fatal("Timed out waiting for event") - } - if expectPass { - if exception != "" { - t.Fatal("Unexpected exception", exception) - } - evv := ev.(types.EventDataCall) - ret := evv.Return - if err := f(ret); err != nil { - t.Fatal(err) - } - } else { - if exception == "" { - t.Fatal("Expected exception") - } - } -} - -func testSNativeTxExpectFail(t *testing.T, blockCache *BlockCache, snativeArgs ptypes.PermArgs) { - testSNativeTx(t, false, blockCache, 0, snativeArgs) -} - -func testSNativeTxExpectPass(t *testing.T, blockCache *BlockCache, perm ptypes.PermFlag, snativeArgs ptypes.PermArgs) { - testSNativeTx(t, true, blockCache, perm, snativeArgs) -} - -func testSNativeTx(t *testing.T, expectPass bool, blockCache *BlockCache, perm ptypes.PermFlag, snativeArgs ptypes.PermArgs) { - if expectPass { - acc := blockCache.GetAccount(user[0].Address) - acc.Permissions.Base.Set(perm, true) - blockCache.UpdateAccount(acc) - } - tx, _ := types.NewPermissionsTx(blockCache, user[0].PubKey, snativeArgs) - tx.Sign(chainID, user[0]) - err := ExecTx(blockCache, tx, true, nil) - if expectPass { - if err != nil { - t.Fatal("Unexpected exception", err) - } - } else { - if err == nil { - t.Fatal("Expected exception") - } - } -} - -func boolToWord256(v bool) Word256 { - var vint byte - if v { - vint = 0x1 - } else { - vint = 0x0 - } - return LeftPadWord256([]byte{vint}) -} - -func snativePermTestInputCALL(name string, user *acm.PrivAccount, perm ptypes.PermFlag, val bool) (addr []byte, data []byte) { - addr = LeftPadWord256([]byte(name)).Postfix(20) - switch name { - case "has_base", "unset_base": - data = LeftPadBytes(user.Address, 32) - data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) - case "set_base": - data = LeftPadBytes(user.Address, 32) - data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) - data = append(data, boolToWord256(val).Bytes()...) - case "set_global": - data = Uint64ToWord256(uint64(perm)).Bytes() - data = append(data, boolToWord256(val).Bytes()...) - } - return -} - -func snativePermTestInputTx(name string, user *acm.PrivAccount, perm ptypes.PermFlag, val bool) (snativeArgs ptypes.PermArgs) { - switch name { - case "has_base": - snativeArgs = &ptypes.HasBaseArgs{user.Address, perm} - case "unset_base": - snativeArgs = &ptypes.UnsetBaseArgs{user.Address, perm} - case "set_base": - snativeArgs = &ptypes.SetBaseArgs{user.Address, perm, val} - case "set_global": - snativeArgs = &ptypes.SetGlobalArgs{perm, val} - } - return -} - -func snativeRoleTestInputCALL(name string, user *acm.PrivAccount, role string) (addr []byte, data []byte) { - addr = LeftPadWord256([]byte(name)).Postfix(20) - data = LeftPadBytes(user.Address, 32) - data = append(data, LeftPadBytes([]byte(role), 32)...) - return -} - -func snativeRoleTestInputTx(name string, user *acm.PrivAccount, role string) (snativeArgs ptypes.PermArgs) { - switch name { - case "has_role": - snativeArgs = &ptypes.HasRoleArgs{user.Address, role} - case "add_role": - snativeArgs = &ptypes.AddRoleArgs{user.Address, role} - case "rm_role": - snativeArgs = &ptypes.RmRoleArgs{user.Address, role} - } - return -} - -// convenience function for contract that calls a given address -func callContractCode(contractAddr []byte) []byte { - // calldatacopy into mem and use as input to call - memOff, inputOff := byte(0x0), byte(0x0) - contractCode := []byte{0x36, 0x60, inputOff, 0x60, memOff, 0x37} - - gas1, gas2 := byte(0x1), byte(0x1) - value := byte(0x1) - inOff := byte(0x0) - retOff, retSize := byte(0x0), byte(0x20) - // this is the code we want to run (call a contract and return) - contractCode = append(contractCode, []byte{0x60, retSize, 0x60, retOff, 0x36, 0x60, inOff, 0x60, value, 0x73}...) - contractCode = append(contractCode, contractAddr...) - contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20, 0x60, 0x0, 0xf3}...) - return contractCode -} - -// convenience function for contract that is a factory for the code that comes as call data -func createContractCode() []byte { - // TODO: gas ... - - // calldatacopy the calldatasize - memOff, inputOff := byte(0x0), byte(0x0) - contractCode := []byte{0x60, memOff, 0x60, inputOff, 0x36, 0x37} - - // create - value := byte(0x1) - contractCode = append(contractCode, []byte{0x60, value, 0x36, 0x60, memOff, 0xf0}...) - return contractCode -} - -// wrap a contract in create code -func wrapContractForCreate(contractCode []byte) []byte { - // 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{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(lenCode), 0x60, 0x0, 0xf3}...) - // return init code, contract code, expected return - return code -} diff --git a/state/state.go b/state/state.go index 3dd2044d9..d77d9a172 100644 --- a/state/state.go +++ b/state/state.go @@ -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 } diff --git a/state/state_test.go b/state/state_test.go deleted file mode 100644 index c98aac82a..000000000 --- a/state/state_test.go +++ /dev/null @@ -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) - } -} diff --git a/state/tx_cache.go b/state/tx_cache.go deleted file mode 100644 index 8330f7b25..000000000 --- a/state/tx_cache.go +++ /dev/null @@ -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 -} diff --git a/state/tx_cache_test.go b/state/tx_cache_test.go deleted file mode 100644 index 85e67f4cb..000000000 --- a/state/tx_cache_test.go +++ /dev/null @@ -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) - } - -} diff --git a/state/types/genesis.go b/state/types/genesis.go deleted file mode 100644 index fe73156b5..000000000 --- a/state/types/genesis.go +++ /dev/null @@ -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 - -} diff --git a/types/block.go b/types/block.go index 20fc11785..40b580820 100644 --- a/types/block.go +++ b/types/block.go @@ -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 } diff --git a/types/genesis.go b/types/genesis.go new file mode 100644 index 000000000..96bdf5052 --- /dev/null +++ b/types/genesis.go @@ -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 + +} diff --git a/types/names.go b/types/names.go deleted file mode 100644 index 2f4c8ff83..000000000 --- a/types/names.go +++ /dev/null @@ -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 -} diff --git a/types/node.go b/types/node.go deleted file mode 100644 index f3c31a474..000000000 --- a/types/node.go +++ /dev/null @@ -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 -} diff --git a/types/priv_validator.go b/types/priv_validator.go index f2a31c10e..67189c7c4 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -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) } diff --git a/types/proposal.go b/types/proposal.go index 5d788e7b0..bd40b8502 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -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 { diff --git a/types/proposal_test.go b/types/proposal_test.go index 24692c3a1..725d5584c 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -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}}`, diff --git a/types/signable.go b/types/signable.go new file mode 100644 index 000000000..fddf6e438 --- /dev/null +++ b/types/signable.go @@ -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)) +} diff --git a/types/tx.go b/types/tx.go index cc7068b84..a3cb9fc04 100644 --- a/types/tx.go +++ b/types/tx.go @@ -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 diff --git a/types/tx_test.go b/types/tx_test.go deleted file mode 100644 index ee13d7baa..000000000 --- a/types/tx_test.go +++ /dev/null @@ -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) - } -} diff --git a/types/tx_utils.go b/types/tx_utils.go deleted file mode 100644 index 3750483da..000000000 --- a/types/tx_utils.go +++ /dev/null @@ -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) -} diff --git a/types/validator.go b/types/validator.go index 8c3648047..1565a371e 100644 --- a/types/validator.go +++ b/types/validator.go @@ -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) } diff --git a/types/validator_set.go b/types/validator_set.go index f2b35a8f0..763a6ce2d 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -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) } diff --git a/types/validator_set_test.go b/types/validator_set_test.go index eca57d778..c20f010c4 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -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") diff --git a/types/vote.go b/types/vote.go index 1c4763e54..48ce10023 100644 --- a/types/vote.go +++ b/types/vote.go @@ -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 diff --git a/types/vote_set.go b/types/vote_set.go index efd597173..470aaf598 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -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 } diff --git a/types/vote_set_test.go b/types/vote_set_test.go index c97c640f2..92686c91e 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -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 } diff --git a/vm/.ethtest b/vm/.ethtest deleted file mode 100644 index e69de29bb..000000000 diff --git a/vm/common.go b/vm/common.go deleted file mode 100644 index 00ab93c77..000000000 --- a/vm/common.go +++ /dev/null @@ -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) - } -} diff --git a/vm/gas.go b/vm/gas.go deleted file mode 100644 index fe4a43d80..000000000 --- a/vm/gas.go +++ /dev/null @@ -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 -) diff --git a/vm/log.go b/vm/log.go deleted file mode 100644 index 82880ae97..000000000 --- a/vm/log.go +++ /dev/null @@ -1,7 +0,0 @@ -package vm - -import ( - "github.com/tendermint/go-logger" -) - -var log = logger.New("module", "vm") diff --git a/vm/native.go b/vm/native.go deleted file mode 100644 index 50c9c8961..000000000 --- a/vm/native.go +++ /dev/null @@ -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 -} diff --git a/vm/opcodes.go b/vm/opcodes.go deleted file mode 100644 index 87e09bfdd..000000000 --- a/vm/opcodes.go +++ /dev/null @@ -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 -} diff --git a/vm/randentropy/rand_entropy.go b/vm/randentropy/rand_entropy.go deleted file mode 100644 index 615fee35d..000000000 --- a/vm/randentropy/rand_entropy.go +++ /dev/null @@ -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 -} diff --git a/vm/sha3/keccakf.go b/vm/sha3/keccakf.go deleted file mode 100644 index 3baf13ba3..000000000 --- a/vm/sha3/keccakf.go +++ /dev/null @@ -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]<>(64-ro_06) - d.b[2] = d.a[12]<>(64-ro_12) - d.b[3] = d.a[18]<>(64-ro_18) - d.b[4] = d.a[24]<>(64-ro_24) - d.b[5] = d.a[3]<>(64-ro_15) - d.b[6] = d.a[9]<>(64-ro_21) - d.b[7] = d.a[10]<>(64-ro_02) - d.b[8] = d.a[16]<>(64-ro_08) - d.b[9] = d.a[22]<>(64-ro_14) - d.b[10] = d.a[1]<>(64-ro_05) - d.b[11] = d.a[7]<>(64-ro_11) - d.b[12] = d.a[13]<>(64-ro_17) - d.b[13] = d.a[19]<>(64-ro_23) - d.b[14] = d.a[20]<>(64-ro_04) - d.b[15] = d.a[4]<>(64-ro_20) - d.b[16] = d.a[5]<>(64-ro_01) - d.b[17] = d.a[11]<>(64-ro_07) - d.b[18] = d.a[17]<>(64-ro_13) - d.b[19] = d.a[23]<>(64-ro_19) - d.b[20] = d.a[2]<>(64-ro_10) - d.b[21] = d.a[8]<>(64-ro_16) - d.b[22] = d.a[14]<>(64-ro_22) - d.b[23] = d.a[15]<>(64-ro_03) - d.b[24] = 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 - } -} diff --git a/vm/sha3/sha3.go b/vm/sha3/sha3.go deleted file mode 100644 index da6b381f4..000000000 --- a/vm/sha3/sha3.go +++ /dev/null @@ -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) -} diff --git a/vm/snative.go b/vm/snative.go deleted file mode 100644 index d5a8a519e..000000000 --- a/vm/snative.go +++ /dev/null @@ -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 -} diff --git a/vm/stack.go b/vm/stack.go deleted file mode 100644 index 979aba2e3..000000000 --- a/vm/stack.go +++ /dev/null @@ -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("#############") -} diff --git a/vm/test/fake_app_state.go b/vm/test/fake_app_state.go deleted file mode 100644 index 78dc6745a..000000000 --- a/vm/test/fake_app_state.go +++ /dev/null @@ -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]) -} diff --git a/vm/test/log_event_test.go b/vm/test/log_event_test.go deleted file mode 100644 index cd03b7141..000000000 --- a/vm/test/log_event_test.go +++ /dev/null @@ -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) - } -} diff --git a/vm/test/vm_test.go b/vm/test/vm_test.go deleted file mode 100644 index 846d9fe9a..000000000 --- a/vm/test/vm_test.go +++ /dev/null @@ -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} -*/ diff --git a/vm/types.go b/vm/types.go deleted file mode 100644 index 65c0724ea..000000000 --- a/vm/types.go +++ /dev/null @@ -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 -} diff --git a/vm/vm.go b/vm/vm.go deleted file mode 100644 index ca11f9340..000000000 --- a/vm/vm.go +++ /dev/null @@ -1,934 +0,0 @@ -package vm - -import ( - "bytes" - "errors" - "fmt" - "math/big" - - . "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/sha3" -) - -var ( - ErrUnknownAddress = errors.New("Unknown address") - ErrInsufficientBalance = errors.New("Insufficient balance") - ErrInvalidJumpDest = errors.New("Invalid jump dest") - ErrInsufficientGas = errors.New("Insuffient gas") - ErrMemoryOutOfBounds = errors.New("Memory out of bounds") - ErrCodeOutOfBounds = errors.New("Code out of bounds") - ErrInputOutOfBounds = errors.New("Input out of bounds") - ErrCallStackOverflow = errors.New("Call stack overflow") - ErrCallStackUnderflow = errors.New("Call stack underflow") - ErrDataStackOverflow = errors.New("Data stack overflow") - ErrDataStackUnderflow = errors.New("Data stack underflow") - ErrInvalidContract = errors.New("Invalid contract") -) - -type ErrPermission struct { - typ string -} - -func (err ErrPermission) Error() string { - return fmt.Sprintf("Contract does not have permission to %s", err.typ) -} - -const ( - dataStackCapacity = 1024 - callStackCapacity = 100 // TODO ensure usage. - memoryCapacity = 1024 * 1024 // 1 MB -) - -type Debug bool - -var dbg Debug - -func SetDebug(d bool) { - dbg = Debug(d) -} - -func (d Debug) Printf(s string, a ...interface{}) { - if d { - fmt.Printf(s, a...) - } -} - -type VM struct { - appState AppState - params Params - origin Word256 - txid []byte - - callDepth int - - evc events.Fireable -} - -func NewVM(appState AppState, params Params, origin Word256, txid []byte) *VM { - return &VM{ - appState: appState, - params: params, - origin: origin, - callDepth: 0, - txid: txid, - } -} - -// satisfies events.Eventable -func (vm *VM) SetFireable(evc events.Fireable) { - vm.evc = evc -} - -// CONTRACT: it is the duty of the contract writer to call known permissions -// we do not convey if a permission is not set -// (unlike in state/execution, where we guarantee HasPermission is called -// on known permissions and panics else) -// If the perm is not defined in the acc nor set by default in GlobalPermissions, -// prints a log warning and returns false. -func HasPermission(appState AppState, acc *Account, perm ptypes.PermFlag) bool { - v, err := acc.Permissions.Base.Get(perm) - if _, ok := err.(ptypes.ErrValueNotSet); ok { - if appState == nil { - log.Warn(Fmt("\n\n***** Unknown permission %b! ********\n\n", perm)) - return false - } - return HasPermission(nil, appState.GetAccount(ptypes.GlobalPermissionsAddress256), perm) - } - return v -} - -func (vm *VM) fireCallEvent(exception *string, output *[]byte, caller, callee *Account, input []byte, value int64, gas *int64) { - // fire the post call event (including exception if applicable) - if vm.evc != nil { - vm.evc.FireEvent(types.EventStringAccCall(callee.Address.Postfix(20)), types.EventDataCall{ - &types.CallData{caller.Address.Postfix(20), callee.Address.Postfix(20), input, value, *gas}, - vm.origin.Postfix(20), - vm.txid, - *output, - *exception, - }) - } -} - -// CONTRACT appState is aware of caller and callee, so we can just mutate them. -// CONTRACT code and input are not mutated. -// CONTRACT returned 'ret' is a new compact slice. -// value: To be transferred from caller to callee. Refunded upon error. -// gas: Available gas. No refunds for gas. -// code: May be nil, since the CALL opcode may be used to send value from contracts to accounts -func (vm *VM) Call(caller, callee *Account, code, input []byte, value int64, gas *int64) (output []byte, err error) { - - exception := new(string) - // fire the post call event (including exception if applicable) - defer vm.fireCallEvent(exception, &output, caller, callee, input, value, gas) - - if err = transfer(caller, callee, value); err != nil { - *exception = err.Error() - return - } - - if len(code) > 0 { - vm.callDepth += 1 - output, err = vm.call(caller, callee, code, input, value, gas) - vm.callDepth -= 1 - if err != nil { - *exception = err.Error() - err := transfer(callee, caller, value) - if err != nil { - // data has been corrupted in ram - PanicCrisis("Could not return value to caller") - } - } - } - - return -} - -// Try to deduct gasToUse from gasLeft. If ok return false, otherwise -// set err and return true. -func useGasNegative(gasLeft *int64, gasToUse int64, err *error) bool { - if *gasLeft >= gasToUse { - *gasLeft -= gasToUse - return false - } else if *err == nil { - *err = ErrInsufficientGas - } - return true -} - -// Just like Call() but does not transfer 'value' or modify the callDepth. -func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas *int64) (output []byte, err error) { - dbg.Printf("(%d) (%X) %X (code=%d) gas: %v (d) %X\n", vm.callDepth, caller.Address[:4], callee.Address, len(callee.Code), *gas, input) - - var ( - pc int64 = 0 - stack = NewStack(dataStackCapacity, gas, &err) - memory = make([]byte, memoryCapacity) - ) - - for { - - // Use BaseOp gas. - if useGasNegative(gas, GasBaseOp, &err) { - return nil, err - } - - var op = codeGetOp(code, pc) - dbg.Printf("(pc) %-3d (op) %-14s (st) %-4d ", pc, op.String(), stack.Len()) - - switch op { - - case ADD: // 0x01 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - sum := new(big.Int).Add(xb, yb) - res := LeftPadWord256(U256(sum).Bytes()) - stack.Push(res) - dbg.Printf(" %v + %v = %v (%X)\n", xb, yb, sum, res) - - case MUL: // 0x02 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - prod := new(big.Int).Mul(xb, yb) - res := LeftPadWord256(U256(prod).Bytes()) - stack.Push(res) - dbg.Printf(" %v * %v = %v (%X)\n", xb, yb, prod, res) - - case SUB: // 0x03 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - diff := new(big.Int).Sub(xb, yb) - res := LeftPadWord256(U256(diff).Bytes()) - stack.Push(res) - dbg.Printf(" %v - %v = %v (%X)\n", xb, yb, diff, res) - - case DIV: // 0x04 - x, y := stack.Pop(), stack.Pop() - if y.IsZero() { - stack.Push(Zero256) - dbg.Printf(" %x / %x = %v\n", x, y, 0) - } else { - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - div := new(big.Int).Div(xb, yb) - res := LeftPadWord256(U256(div).Bytes()) - stack.Push(res) - dbg.Printf(" %v / %v = %v (%X)\n", xb, yb, div, res) - } - - case SDIV: // 0x05 - x, y := stack.Pop(), stack.Pop() - if y.IsZero() { - stack.Push(Zero256) - dbg.Printf(" %x / %x = %v\n", x, y, 0) - } else { - xb := S256(new(big.Int).SetBytes(x[:])) - yb := S256(new(big.Int).SetBytes(y[:])) - div := new(big.Int).Div(xb, yb) - res := LeftPadWord256(U256(div).Bytes()) - stack.Push(res) - dbg.Printf(" %v / %v = %v (%X)\n", xb, yb, div, res) - } - - case MOD: // 0x06 - x, y := stack.Pop(), stack.Pop() - if y.IsZero() { - stack.Push(Zero256) - dbg.Printf(" %v %% %v = %v\n", x, y, 0) - } else { - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - mod := new(big.Int).Mod(xb, yb) - res := LeftPadWord256(U256(mod).Bytes()) - stack.Push(res) - dbg.Printf(" %v %% %v = %v (%X)\n", xb, yb, mod, res) - } - - case SMOD: // 0x07 - x, y := stack.Pop(), stack.Pop() - if y.IsZero() { - stack.Push(Zero256) - dbg.Printf(" %v %% %v = %v\n", x, y, 0) - } else { - xb := S256(new(big.Int).SetBytes(x[:])) - yb := S256(new(big.Int).SetBytes(y[:])) - mod := new(big.Int).Mod(xb, yb) - res := LeftPadWord256(U256(mod).Bytes()) - stack.Push(res) - dbg.Printf(" %v %% %v = %v (%X)\n", xb, yb, mod, res) - } - - case ADDMOD: // 0x08 - x, y, z := stack.Pop(), stack.Pop(), stack.Pop() - if z.IsZero() { - stack.Push(Zero256) - dbg.Printf(" %v %% %v = %v\n", x, y, 0) - } else { - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - zb := new(big.Int).SetBytes(z[:]) - add := new(big.Int).Add(xb, yb) - mod := new(big.Int).Mod(add, zb) - res := LeftPadWord256(U256(mod).Bytes()) - stack.Push(res) - dbg.Printf(" %v + %v %% %v = %v (%X)\n", - xb, yb, zb, mod, res) - } - - case MULMOD: // 0x09 - x, y, z := stack.Pop(), stack.Pop(), stack.Pop() - if z.IsZero() { - stack.Push(Zero256) - dbg.Printf(" %v %% %v = %v\n", x, y, 0) - } else { - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - zb := new(big.Int).SetBytes(z[:]) - mul := new(big.Int).Mul(xb, yb) - mod := new(big.Int).Mod(mul, zb) - res := LeftPadWord256(U256(mod).Bytes()) - stack.Push(res) - dbg.Printf(" %v * %v %% %v = %v (%X)\n", - xb, yb, zb, mod, res) - } - - case EXP: // 0x0A - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - pow := new(big.Int).Exp(xb, yb, big.NewInt(0)) - res := LeftPadWord256(U256(pow).Bytes()) - stack.Push(res) - dbg.Printf(" %v ** %v = %v (%X)\n", xb, yb, pow, res) - - case SIGNEXTEND: // 0x0B - back := stack.Pop() - backb := new(big.Int).SetBytes(back[:]) - if backb.Cmp(big.NewInt(31)) < 0 { - bit := uint(backb.Uint64()*8 + 7) - num := stack.Pop() - numb := new(big.Int).SetBytes(num[:]) - mask := new(big.Int).Lsh(big.NewInt(1), bit) - mask.Sub(mask, big.NewInt(1)) - if numb.Bit(int(bit)) == 1 { - numb.Or(numb, mask.Not(mask)) - } else { - numb.Add(numb, mask) - } - res := LeftPadWord256(U256(numb).Bytes()) - dbg.Printf(" = %v (%X)", numb, res) - stack.Push(res) - } - - case LT: // 0x10 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - if xb.Cmp(yb) < 0 { - stack.Push64(1) - dbg.Printf(" %v < %v = %v\n", xb, yb, 1) - } else { - stack.Push(Zero256) - dbg.Printf(" %v < %v = %v\n", xb, yb, 0) - } - - case GT: // 0x11 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - if xb.Cmp(yb) > 0 { - stack.Push64(1) - dbg.Printf(" %v > %v = %v\n", xb, yb, 1) - } else { - stack.Push(Zero256) - dbg.Printf(" %v > %v = %v\n", xb, yb, 0) - } - - case SLT: // 0x12 - x, y := stack.Pop(), stack.Pop() - xb := S256(new(big.Int).SetBytes(x[:])) - yb := S256(new(big.Int).SetBytes(y[:])) - if xb.Cmp(yb) < 0 { - stack.Push64(1) - dbg.Printf(" %v < %v = %v\n", xb, yb, 1) - } else { - stack.Push(Zero256) - dbg.Printf(" %v < %v = %v\n", xb, yb, 0) - } - - case SGT: // 0x13 - x, y := stack.Pop(), stack.Pop() - xb := S256(new(big.Int).SetBytes(x[:])) - yb := S256(new(big.Int).SetBytes(y[:])) - if xb.Cmp(yb) > 0 { - stack.Push64(1) - dbg.Printf(" %v > %v = %v\n", xb, yb, 1) - } else { - stack.Push(Zero256) - dbg.Printf(" %v > %v = %v\n", xb, yb, 0) - } - - case EQ: // 0x14 - x, y := stack.Pop(), stack.Pop() - if bytes.Equal(x[:], y[:]) { - stack.Push64(1) - dbg.Printf(" %X == %X = %v\n", x, y, 1) - } else { - stack.Push(Zero256) - dbg.Printf(" %X == %X = %v\n", x, y, 0) - } - - case ISZERO: // 0x15 - x := stack.Pop() - if x.IsZero() { - stack.Push64(1) - dbg.Printf(" %v == 0 = %v\n", x, 1) - } else { - stack.Push(Zero256) - dbg.Printf(" %v == 0 = %v\n", x, 0) - } - - case AND: // 0x16 - x, y := stack.Pop(), stack.Pop() - z := [32]byte{} - for i := 0; i < 32; i++ { - z[i] = x[i] & y[i] - } - stack.Push(z) - dbg.Printf(" %X & %X = %X\n", x, y, z) - - case OR: // 0x17 - x, y := stack.Pop(), stack.Pop() - z := [32]byte{} - for i := 0; i < 32; i++ { - z[i] = x[i] | y[i] - } - stack.Push(z) - dbg.Printf(" %X | %X = %X\n", x, y, z) - - case XOR: // 0x18 - x, y := stack.Pop(), stack.Pop() - z := [32]byte{} - for i := 0; i < 32; i++ { - z[i] = x[i] ^ y[i] - } - stack.Push(z) - dbg.Printf(" %X ^ %X = %X\n", x, y, z) - - case NOT: // 0x19 - x := stack.Pop() - z := [32]byte{} - for i := 0; i < 32; i++ { - z[i] = ^x[i] - } - stack.Push(z) - dbg.Printf(" !%X = %X\n", x, z) - - case BYTE: // 0x1A - idx, val := stack.Pop64(), stack.Pop() - res := byte(0) - if idx < 32 { - res = val[idx] - } - stack.Push64(int64(res)) - dbg.Printf(" => 0x%X\n", res) - - case SHA3: // 0x20 - if useGasNegative(gas, GasSha3, &err) { - return nil, err - } - offset, size := stack.Pop64(), stack.Pop64() - data, ok := subslice(memory, offset, size) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - data = sha3.Sha3(data) - stack.PushBytes(data) - dbg.Printf(" => (%v) %X\n", size, data) - - case ADDRESS: // 0x30 - stack.Push(callee.Address) - dbg.Printf(" => %X\n", callee.Address) - - case BALANCE: // 0x31 - addr := stack.Pop() - if useGasNegative(gas, GasGetAccount, &err) { - return nil, err - } - acc := vm.appState.GetAccount(addr) - if acc == nil { - return nil, firstErr(err, ErrUnknownAddress) - } - balance := acc.Balance - stack.Push64(balance) - dbg.Printf(" => %v (%X)\n", balance, addr) - - case ORIGIN: // 0x32 - stack.Push(vm.origin) - dbg.Printf(" => %X\n", vm.origin) - - case CALLER: // 0x33 - stack.Push(caller.Address) - dbg.Printf(" => %X\n", caller.Address) - - case CALLVALUE: // 0x34 - stack.Push64(value) - dbg.Printf(" => %v\n", value) - - case CALLDATALOAD: // 0x35 - offset := stack.Pop64() - data, ok := subslice(input, offset, 32) - if !ok { - return nil, firstErr(err, ErrInputOutOfBounds) - } - res := LeftPadWord256(data) - stack.Push(res) - dbg.Printf(" => 0x%X\n", res) - - case CALLDATASIZE: // 0x36 - stack.Push64(int64(len(input))) - dbg.Printf(" => %d\n", len(input)) - - case CALLDATACOPY: // 0x37 - memOff := stack.Pop64() - inputOff := stack.Pop64() - length := stack.Pop64() - data, ok := subslice(input, inputOff, length) - if !ok { - return nil, firstErr(err, ErrInputOutOfBounds) - } - dest, ok := subslice(memory, memOff, length) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - copy(dest, data) - dbg.Printf(" => [%v, %v, %v] %X\n", memOff, inputOff, length, data) - - case CODESIZE: // 0x38 - l := int64(len(code)) - stack.Push64(l) - dbg.Printf(" => %d\n", l) - - case CODECOPY: // 0x39 - memOff := stack.Pop64() - codeOff := stack.Pop64() - length := stack.Pop64() - data, ok := subslice(code, codeOff, length) - if !ok { - return nil, firstErr(err, ErrCodeOutOfBounds) - } - dest, ok := subslice(memory, memOff, length) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - copy(dest, data) - dbg.Printf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data) - - case GASPRICE_DEPRECATED: // 0x3A - stack.Push(Zero256) - dbg.Printf(" => %X (GASPRICE IS DEPRECATED)\n") - - case EXTCODESIZE: // 0x3B - addr := stack.Pop() - if useGasNegative(gas, GasGetAccount, &err) { - return nil, err - } - acc := vm.appState.GetAccount(addr) - if acc == nil { - return nil, firstErr(err, ErrUnknownAddress) - } - code := acc.Code - l := int64(len(code)) - stack.Push64(l) - dbg.Printf(" => %d\n", l) - - case EXTCODECOPY: // 0x3C - addr := stack.Pop() - if useGasNegative(gas, GasGetAccount, &err) { - return nil, err - } - acc := vm.appState.GetAccount(addr) - if acc == nil { - return nil, firstErr(err, ErrUnknownAddress) - } - code := acc.Code - memOff := stack.Pop64() - codeOff := stack.Pop64() - length := stack.Pop64() - data, ok := subslice(code, codeOff, length) - if !ok { - return nil, firstErr(err, ErrCodeOutOfBounds) - } - dest, ok := subslice(memory, memOff, length) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - copy(dest, data) - dbg.Printf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data) - - case BLOCKHASH: // 0x40 - stack.Push(Zero256) - dbg.Printf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes()) - - case COINBASE: // 0x41 - stack.Push(Zero256) - dbg.Printf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes()) - - case TIMESTAMP: // 0x42 - time := vm.params.BlockTime - stack.Push64(int64(time)) - dbg.Printf(" => 0x%X\n", time) - - case BLOCKHEIGHT: // 0x43 - number := int64(vm.params.BlockHeight) - stack.Push64(number) - dbg.Printf(" => 0x%X\n", number) - - case GASLIMIT: // 0x45 - stack.Push64(vm.params.GasLimit) - dbg.Printf(" => %v\n", vm.params.GasLimit) - - case POP: // 0x50 - popped := stack.Pop() - dbg.Printf(" => 0x%X\n", popped) - - case MLOAD: // 0x51 - offset := stack.Pop64() - data, ok := subslice(memory, offset, 32) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - stack.Push(LeftPadWord256(data)) - dbg.Printf(" => 0x%X\n", data) - - case MSTORE: // 0x52 - offset, data := stack.Pop64(), stack.Pop() - dest, ok := subslice(memory, offset, 32) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - copy(dest, data[:]) - dbg.Printf(" => 0x%X\n", data) - - case MSTORE8: // 0x53 - offset, val := stack.Pop64(), byte(stack.Pop64()&0xFF) - if len(memory) <= int(offset) { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - memory[offset] = val - dbg.Printf(" => [%v] 0x%X\n", offset, val) - - case SLOAD: // 0x54 - loc := stack.Pop() - data := vm.appState.GetStorage(callee.Address, loc) - stack.Push(data) - dbg.Printf(" {0x%X : 0x%X}\n", loc, data) - - case SSTORE: // 0x55 - loc, data := stack.Pop(), stack.Pop() - if useGasNegative(gas, GasStorageUpdate, &err) { - return nil, err - } - vm.appState.SetStorage(callee.Address, loc, data) - dbg.Printf(" {0x%X : 0x%X}\n", loc, data) - - case JUMP: // 0x56 - err = jump(code, stack.Pop64(), &pc) - continue - - case JUMPI: // 0x57 - pos, cond := stack.Pop64(), stack.Pop() - if !cond.IsZero() { - err = jump(code, pos, &pc) - continue - } - dbg.Printf(" ~> false\n") - - case PC: // 0x58 - stack.Push64(pc) - - case MSIZE: // 0x59 - stack.Push64(int64(len(memory))) - - case GAS: // 0x5A - stack.Push64(*gas) - dbg.Printf(" => %X\n", *gas) - - case JUMPDEST: // 0x5B - dbg.Printf("\n") - // Do nothing - - 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 := int64(op - PUSH1 + 1) - codeSegment, ok := subslice(code, pc+1, a) - if !ok { - return nil, firstErr(err, ErrCodeOutOfBounds) - } - res := LeftPadWord256(codeSegment) - stack.Push(res) - pc += a - dbg.Printf(" => 0x%X\n", res) - //stack.Print(10) - - case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: - n := int(op - DUP1 + 1) - stack.Dup(n) - dbg.Printf(" => [%d] 0x%X\n", n, stack.Peek().Bytes()) - - case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: - n := int(op - SWAP1 + 2) - stack.Swap(n) - dbg.Printf(" => [%d] %X\n", n, stack.Peek()) - //stack.Print(10) - - case LOG0, LOG1, LOG2, LOG3, LOG4: - n := int(op - LOG0) - topics := make([]Word256, n) - offset, size := stack.Pop64(), stack.Pop64() - for i := 0; i < n; i++ { - topics[i] = stack.Pop() - } - data, ok := subslice(memory, offset, size) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - data = copyslice(data) - if vm.evc != nil { - eventID := types.EventStringLogEvent(callee.Address.Postfix(20)) - fmt.Printf("eventID: %s\n", eventID) - log := types.EventDataLog{ - callee.Address, - topics, - data, - vm.params.BlockHeight, - } - vm.evc.FireEvent(eventID, log) - } - dbg.Printf(" => T:%X D:%X\n", topics, data) - - case CREATE: // 0xF0 - if !HasPermission(vm.appState, callee, ptypes.CreateContract) { - return nil, ErrPermission{"create_contract"} - } - contractValue := stack.Pop64() - offset, size := stack.Pop64(), stack.Pop64() - input, ok := subslice(memory, offset, size) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - - // Check balance - if callee.Balance < contractValue { - return nil, firstErr(err, ErrInsufficientBalance) - } - - // TODO charge for gas to create account _ the code length * GasCreateByte - - newAccount := vm.appState.CreateAccount(callee) - // Run the input to get the contract code. - // NOTE: no need to copy 'input' as per Call contract. - ret, err_ := vm.Call(callee, newAccount, input, input, contractValue, gas) - if err_ != nil { - stack.Push(Zero256) - } else { - newAccount.Code = ret // Set the code (ret need not be copied as per Call contract) - stack.Push(newAccount.Address) - } - - case CALL, CALLCODE: // 0xF1, 0xF2 - if !HasPermission(vm.appState, callee, ptypes.Call) { - return nil, ErrPermission{"call"} - } - gasLimit := stack.Pop64() - addr, value := stack.Pop(), stack.Pop64() - inOffset, inSize := stack.Pop64(), stack.Pop64() // inputs - retOffset, retSize := stack.Pop64(), stack.Pop64() // outputs - dbg.Printf(" => %X\n", addr) - - // Get the arguments from the memory - args, ok := subslice(memory, inOffset, inSize) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - args = copyslice(args) - - // Ensure that gasLimit is reasonable - if *gas < gasLimit { - return nil, firstErr(err, ErrInsufficientGas) - } else { - *gas -= gasLimit - // NOTE: we will return any used gas later. - } - - // Begin execution - var ret []byte - var err error - if nativeContract := registeredNativeContracts[addr]; nativeContract != nil { - // Native contract - ret, err = nativeContract(vm.appState, callee, args, &gasLimit) - - // for now we fire the Call event. maybe later we'll fire more particulars - var exception string - if err != nil { - exception = err.Error() - } - // NOTE: these fire call events and not particular events for eg name reg or permissions - vm.fireCallEvent(&exception, &ret, callee, &Account{Address: addr}, args, value, gas) - } else { - // EVM contract - if useGasNegative(gas, GasGetAccount, &err) { - return nil, err - } - acc := vm.appState.GetAccount(addr) - // since CALL is used also for sending funds, - // acc may not exist yet. This is an error for - // CALLCODE, but not for CALL, though I don't think - // ethereum actually cares - if op == CALLCODE { - if acc == nil { - return nil, firstErr(err, ErrUnknownAddress) - } - ret, err = vm.Call(callee, callee, acc.Code, args, value, gas) - } else { - if acc == nil { - // nil account means we're sending funds to a new account - if !HasPermission(vm.appState, caller, ptypes.CreateAccount) { - return nil, ErrPermission{"create_account"} - } - acc = &Account{Address: addr} - vm.appState.UpdateAccount(acc) - // send funds to new account - ret, err = vm.Call(callee, acc, acc.Code, args, value, gas) - } else { - // call standard contract - ret, err = vm.Call(callee, acc, acc.Code, args, value, gas) - } - } - } - - // Push result - if err != nil { - dbg.Printf("error on call: %s\n", err.Error()) - stack.Push(Zero256) - } else { - stack.Push(One256) - dest, ok := subslice(memory, retOffset, retSize) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - copy(dest, ret) - } - - // Handle remaining gas. - *gas += gasLimit - - dbg.Printf("resume %X (%v)\n", callee.Address, gas) - - case RETURN: // 0xF3 - offset, size := stack.Pop64(), stack.Pop64() - ret, ok := subslice(memory, offset, size) - if !ok { - return nil, firstErr(err, ErrMemoryOutOfBounds) - } - dbg.Printf(" => [%v, %v] (%d) 0x%X\n", offset, size, len(ret), ret) - output = copyslice(ret) - return output, nil - - case SUICIDE: // 0xFF - addr := stack.Pop() - if useGasNegative(gas, GasGetAccount, &err) { - return nil, err - } - // TODO if the receiver is , then make it the fee. (?) - // TODO: create account if doesn't exist (no reason not to) - receiver := vm.appState.GetAccount(addr) - if receiver == nil { - return nil, firstErr(err, ErrUnknownAddress) - } - balance := callee.Balance - receiver.Balance += balance - vm.appState.UpdateAccount(receiver) - vm.appState.RemoveAccount(callee) - dbg.Printf(" => (%X) %v\n", addr[:4], balance) - fallthrough - - case STOP: // 0x00 - return nil, nil - - default: - dbg.Printf("(pc) %-3v Invalid opcode %X\n", pc, op) - return nil, fmt.Errorf("Invalid opcode %X", op) - } - - pc++ - - } -} - -func subslice(data []byte, offset, length int64) (ret []byte, ok bool) { - size := int64(len(data)) - if size < offset { - return nil, false - } else if size < offset+length { - ret, ok = data[offset:], true - ret = RightPadBytes(ret, 32) - } else { - ret, ok = data[offset:offset+length], true - } - return -} - -func copyslice(src []byte) (dest []byte) { - dest = make([]byte, len(src)) - copy(dest, src) - return dest -} - -func rightMostBytes(data []byte, n int) []byte { - size := MinInt(len(data), n) - offset := len(data) - size - return data[offset:] -} - -func codeGetOp(code []byte, n int64) OpCode { - if int64(len(code)) <= n { - return OpCode(0) // stop - } else { - return OpCode(code[n]) - } -} - -func jump(code []byte, to int64, pc *int64) (err error) { - dest := codeGetOp(code, to) - if dest != JUMPDEST { - dbg.Printf(" ~> %v invalid jump dest %v\n", to, dest) - return ErrInvalidJumpDest - } - dbg.Printf(" ~> %v\n", to) - *pc = to - return nil -} - -func firstErr(errA, errB error) error { - if errA != nil { - return errA - } else { - return errB - } -} - -func transfer(from, to *Account, amount int64) error { - if from.Balance < amount { - return ErrInsufficientBalance - } else { - from.Balance -= amount - to.Balance += amount - return nil - } -}