Skip to content
This repository was archived by the owner on Apr 13, 2023. It is now read-only.

Commit a24a2d1

Browse files
authored
jem/20210502 core17 (#435)
* Upgrade to core v17 * Add parser for EffectClaimableBalanceClawedBack * Introduce ClawBackOperation * Add ClawBackClaimableBalanceOperation * Allow setting and clearing of trustline flags
1 parent 92a5a21 commit a24a2d1

File tree

11 files changed

+292
-27
lines changed

11 files changed

+292
-27
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@
22

33
As this project is pre 1.0, breaking changes may happen for minor version bumps. A breaking change will get clearly notified in this log.
44

5-
## Next version
5+
## Next Version
66

7+
## 0.20.0
8+
9+
- Updated for core protocol v17/CAP35:
10+
- Added EffectTrustLineFlagsUpdated
11+
- Added EffectClaimableBalanceClawedBack
12+
- Added ClawBackOperation
13+
- Added ClawBackClaimableBalanceOperation
14+
- Added SetTrustLineFlagsOperation
715
- Auth challenge transactions can now specify separate web auth & home domains. The home domain is the FQDN that hosts
816
the relevant `stellar.toml` file for the authenticating service. The web auth domain is the FQDN of the
917
authenticating service itself. Implements SEP-0010 v3.1.

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# [Stellar SDK for Scala](https://synesso.github.io/scala-stellar-sdk/)
22

3-
![Build](https://github.com/Synesso/scala-stellar-sdk/workflows/Build/badge.svg?branch=master)
3+
[![Build](https://github.com/Synesso/scala-stellar-sdk/workflows/Build/badge.svg?branch=master)](https://github.com/Synesso/scala-stellar-sdk/actions/workflows/scala.yml)
44
[![Coverage](https://img.shields.io/codecov/c/gh/Synesso/scala-stellar-sdk.svg)](https://codecov.io/gh/Synesso/scala-stellar-sdk)
55
[![Issues](https://img.shields.io/github/issues/Synesso/scala-stellar-sdk.svg)](https://github.com/Synesso/scala-stellar-sdk/issues)
6-
![Supports Stellar Horizon v1.14.0](https://img.shields.io/badge/Horizon-v1.14.0-blue.svg)
7-
![Supports Stellar Core v15](https://img.shields.io/badge/Core-v15-blue.svg)
6+
![Supports Stellar Horizon v2.2.0](https://img.shields.io/badge/Horizon-v2.2.0-blue.svg)
7+
![Supports Stellar Core v17](https://img.shields.io/badge/Core-v17-blue.svg)
88
[![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-brightgreen.svg?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org)
99

1010
With the Stellar SDK for Scala, you can perform [Stellar](https://stellar.org/) operations from your Scala application. It provides the ability to access Stellar networks via any Horizon instance to build and submit transactions, query the state of the network and stream updates. You'll like this SDK, because it provides a more natural API for Scala developers than the official Java SDK.
@@ -18,7 +18,7 @@ Add the JitPack & jcenter resolvers and the [latest dependency](https://jitpack.
1818
```scala
1919
resolvers += "jitpack" at "https://jitpack.io"
2020
resolvers += Resolver.jcenterRepo
21-
libraryDependencies += "com.github.synesso" %% "scala-stellar-sdk" % "0.19.2"
21+
libraryDependencies += "com.github.synesso" %% "scala-stellar-sdk" % "0.20.0"
2222
```
2323

2424
From there, it is a simple affair to create and fund a new account on the test network.

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ lazy val root = project
5252
),
5353
libraryDependencies ++= List(
5454
"ch.qos.logback" % "logback-classic" % "1.2.3",
55-
"com.github.synesso" % "stellar-xdr-jre" % "15.1.0.3",
55+
"com.github.synesso" % "stellar-xdr-jre" % "17.0.0",
5656
"com.softwaremill.retry" %% "retry" % "0.3.3",
5757
"com.squareup.okhttp3" % "okhttp" % okhttp,
5858
"com.squareup.okhttp3" % "logging-interceptor" % okhttp,

src/it/bin/stellar_standalone.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22

33
CONTAINER=stellar/quickstart:latest
4-
PROTOCOL_VERSION=15
4+
PROTOCOL_VERSION=17
55

66
function container_started {
77
local state

src/it/scala/stellar/sdk/LocalNetworkIntegrationSpec.scala

Lines changed: 124 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ package stellar.sdk
22

33
import java.io.EOFException
44
import java.time.{Instant, Period}
5-
65
import com.typesafe.scalalogging.LazyLogging
76
import okhttp3.HttpUrl
87
import okio.ByteString
98
import org.json4s.JsonDSL._
109
import org.specs2.concurrent.ExecutionEnv
1110
import org.specs2.mutable.Specification
11+
import org.stellar.xdr.TrustLineFlags
1212
import stellar.sdk.inet.HorizonEntityNotFound
1313
import stellar.sdk.model.Amount.lumens
1414
import stellar.sdk.model.ClaimPredicate.{AbsolutelyBefore, Or, Unconditional}
@@ -40,12 +40,14 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
4040
val accnC = KeyPair.fromPassphrase("account c")
4141
val accnD = KeyPair.fromPassphrase("account d")
4242
val accnE = KeyPair.fromPassphrase("account e")
43+
val accnF = KeyPair.fromPassphrase("account f")
4344

4445
logger.debug(s"Account A = ${accnA.accountId}")
4546
logger.debug(s"Account B = ${accnB.accountId}")
4647
logger.debug(s"Account C = ${accnC.accountId}")
4748
logger.debug(s"Account D = ${accnD.accountId}")
4849
logger.debug(s"Account E = ${accnE.accountId}")
50+
logger.debug(s"Account F = ${accnF.accountId}")
4951

5052
val accounts = Set(accnA, accnB, accnC, accnD)
5153

@@ -101,6 +103,7 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
101103
private val chinchillaA = Asset("Chinchilla", accnA)
102104
private val chinchillaMaster = Asset("Chinchilla", masterAccountKey)
103105
private val dachshundB = Asset("Dachshund", accnB)
106+
private val clawbackAsset = Asset("XYZ", accnF)
104107

105108
// Transaction hashes. These will changed when setup operations change.
106109
private val txnHash2 = "e13447898b27dbf278d4411022e2e6d0aae78ef70670c7af7834a1f2a6d191d8"
@@ -116,6 +119,7 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
116119
CreateAccountOperation(accnB.toAccountId, lumens(1000)),
117120
CreateAccountOperation(accnC.toAccountId, lumens(1000)),
118121
CreateAccountOperation(accnD.toAccountId, lumens(1000)),
122+
CreateAccountOperation(accnF.toAccountId, lumens(1000)),
119123
WriteDataOperation("life_universe_everything", "42", Some(accnB)),
120124
WriteDataOperation("brain the size of a planet", "and they ask me to open a door", Some(accnB)),
121125
WriteDataOperation("fenton", "FENTON!", Some(accnC)),
@@ -272,6 +276,8 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
272276
"list all assets" >> {
273277
val eventualResps = network.assets().map(_.toSeq)
274278
eventualResps must containTheSameElementsAs(Seq(
279+
AssetResponse(aardvarkA, 0, 0, authRequired = true, authRevocable = true),
280+
AssetResponse(beaverA, 0, 0, authRequired = true, authRevocable = true),
275281
AssetResponse(chinchillaA, 101, 1, authRequired = true, authRevocable = true),
276282
AssetResponse(chinchillaMaster, 101, 1, authRequired = false, authRevocable = false),
277283
)).awaitFor(10 seconds)
@@ -285,7 +291,7 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
285291

286292
"filter assets by issuer" >> {
287293
val byIssuer = network.assets(issuer = Some(accnA)).map(_.take(10).toList)
288-
byIssuer.map(_.size) must beEqualTo(1).awaitFor(10 seconds)
294+
byIssuer.map(_.size) must beEqualTo(3).awaitFor(10 seconds)
289295
byIssuer.map(_.map(_.asset.issuer.accountId).distinct) must beEqualTo(Seq(accnA.accountId)).awaitFor(10 seconds)
290296
}
291297

@@ -300,7 +306,7 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
300306
"effect endpoint" should {
301307
"parse all effects" >> {
302308
val effects = network.effects()
303-
effects.map(_.size) must beEqualTo(240).awaitFor(10 seconds)
309+
effects.map(_.size) must beEqualTo(246).awaitFor(10 seconds)
304310
}
305311

306312
"filter effects by account" >> {
@@ -329,13 +335,16 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
329335
EffectAccountDebited(_, _, accn1, amount1),
330336
EffectAccountCredited(_, _, accn2, amount2),
331337
EffectAccountRemoved(_, _, accn3),
332-
EffectTrustLineDeauthorized(_, created, accn4, IssuedAsset12(code, accn5))
338+
EffectTrustLineDeauthorized(_, _, accn4, IssuedAsset12(code, accn5)),
339+
EffectTrustLineFlagsUpdated(_, _, accn6, asset5, false, false, false)
333340
) =>
334341
accn1 must beEquivalentTo(accnC)
335342
accn2 must beEquivalentTo(accnB)
336343
accn3 must beEquivalentTo(accnC)
337344
accn4 must beEquivalentTo(accnB)
338345
accn5 must beEquivalentTo(accnA)
346+
accn6 must beEquivalentTo(accnB)
347+
asset5 mustEqual aardvarkA
339348
amount1 mustEqual lumens(1000)
340349
amount2 mustEqual lumens(1000)
341350
code mustEqual "Aardvark"
@@ -458,7 +467,7 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
458467

459468
"operation endpoint" should {
460469
"list all operations" >> {
461-
network.operations().map(_.size) must beEqualTo(131).awaitFor(10.seconds)
470+
network.operations().map(_.size) must beEqualTo(132).awaitFor(10.seconds)
462471
}
463472

464473
"list operations by account" >> {
@@ -595,8 +604,8 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
595604
byAccount.map(_.head) must beLike[TransactionHistory] {
596605
case t =>
597606
t.account must beEquivalentTo(masterAccountKey)
598-
t.feeCharged mustEqual NativeAmount(1700)
599-
t.operationCount mustEqual 17
607+
t.feeCharged mustEqual NativeAmount(1800)
608+
t.operationCount mustEqual 18
600609
t.memo mustEqual NoMemo
601610
t.ledgerEntries must not(throwAn[EOFException])
602611
t.feeLedgerEntries must not(throwAn[EOFException])
@@ -877,5 +886,113 @@ class LocalNetworkIntegrationSpec(implicit ee: ExecutionEnv) extends Specificati
877886
}.awaitFor(5.seconds)
878887
}
879888
}
889+
890+
"Clawbacks" should {
891+
"be used to revoke funds from an account" >> {
892+
val setClawbackEnabled = for {
893+
account <- network.account(masterAccountKey)
894+
txn = Transaction(
895+
source = account,
896+
operations = List(
897+
SetOptionsOperation(
898+
setFlags = Some(Set(AuthorizationRevocableFlag, AuthorizationClawbackEnabledFlag)),
899+
sourceAccount = Some(accnF)
900+
),
901+
ChangeTrustOperation(IssuedAmount(99_999L, clawbackAsset), Some(accnA)),
902+
PaymentOperation(accnA.toAccountId, IssuedAmount(50_000L, clawbackAsset), Some(accnF))
903+
),
904+
timeBounds = TimeBounds.Unbounded,
905+
maxFee = NativeAmount(1000)
906+
).sign(masterAccountKey, accnF, accnA)
907+
txnResult <- txn.submit()
908+
} yield txnResult
909+
setClawbackEnabled must beLike[TransactionPostResponse] { case r: TransactionApproved =>
910+
r.isSuccess must beTrue
911+
}.awaitFor(60.seconds)
912+
network.account(accnA).map(_.balances) must beLike[List[Balance]] { balances =>
913+
balances must contain(Balance(IssuedAmount(50_000L, clawbackAsset), Some(99_999L), authorized = true, authorizedToMaintainLiabilities = true))
914+
}.awaitFor(3.seconds)
915+
916+
val clawbackTheFunds = for {
917+
account <- network.account(accnF)
918+
txn = Transaction(
919+
source = account,
920+
operations = List(
921+
ClawBackOperation(accnA.toAccountId, IssuedAmount(40_000L, clawbackAsset), Some(accnF))
922+
),
923+
timeBounds = TimeBounds.Unbounded,
924+
maxFee = NativeAmount(1000)
925+
).sign(accnF)
926+
txnResult <- txn.submit()
927+
} yield txnResult
928+
clawbackTheFunds must beLike[TransactionPostResponse] { case r: TransactionApproved =>
929+
r.isSuccess must beTrue
930+
}.awaitFor(60.seconds)
931+
932+
network.account(accnA).map(_.balances) must beLike[List[Balance]] { balances =>
933+
balances must contain(Balance(IssuedAmount(10_000L, clawbackAsset), Some(99_999L), authorized = true, authorizedToMaintainLiabilities = true))
934+
}.awaitFor(3.seconds)
935+
}
936+
937+
"be able to claw back claimable balances" >> {
938+
Await.result(for {
939+
account <- network.account(accnF)
940+
txn = Transaction(
941+
source = account,
942+
operations = List(
943+
CreateClaimableBalanceOperation(
944+
amount = Amount(1000, clawbackAsset),
945+
claimants = List(AccountIdClaimant(accnA, Unconditional))
946+
)
947+
),
948+
timeBounds = TimeBounds.Unbounded,
949+
maxFee = NativeAmount(200)
950+
).sign(accnF)
951+
txnResult <- txn.submit()
952+
} yield txnResult must beLike[TransactionPostResponse] { case r: TransactionApproved =>
953+
r.isSuccess must beTrue
954+
}, 60.seconds)
955+
956+
val clawbackTheFunds = for {
957+
balanceId <- network.claimsByClaimant(accnA).map(_.head.id)
958+
account <- network.account(accnF)
959+
txn = Transaction(
960+
source = account,
961+
operations = List(ClawBackClaimableBalanceOperation(balanceId)),
962+
timeBounds = TimeBounds.Unbounded,
963+
maxFee = NativeAmount(1000)
964+
).sign(accnF)
965+
txnResult <- txn.submit()
966+
} yield txnResult
967+
clawbackTheFunds must beLike[TransactionPostResponse] { case r: TransactionApproved =>
968+
r.isSuccess must beTrue
969+
}.awaitFor(60.seconds)
970+
971+
network.claimsByAsset(dachshundB) must beEmpty[Seq[ClaimableBalance]].awaitFor(10.seconds)
972+
}
973+
}
974+
975+
"Trustline flags" should {
976+
"be settable" >> {
977+
(for {
978+
account <- network.account(accnF)
979+
txn = Transaction(
980+
source = account,
981+
operations = List(SetTrustLineFlagsOperation(
982+
asset = clawbackAsset,
983+
trustor = accnA,
984+
setFlags = Set(TrustLineFlags.AUTHORIZED_FLAG),
985+
clearFlags = Set.empty[TrustLineFlags]
986+
)),
987+
timeBounds = TimeBounds.Unbounded,
988+
maxFee = NativeAmount(1000)
989+
).sign(accnF)
990+
txnResult <- txn.submit()
991+
} yield txnResult) must beLike[TransactionPostResponse] { case r: TransactionApproved =>
992+
r.isSuccess must beTrue
993+
}.awaitFor(60.seconds)
994+
995+
}
996+
}
880997
}
881998

src/main/scala/stellar/sdk/model/Asset.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import okio.ByteString
44
import org.json4s.JsonAST.JValue
55
import org.json4s.{DefaultFormats, Formats}
66
import org.stellar.xdr.Asset.{AssetAlphaNum12, AssetAlphaNum4}
7-
import org.stellar.xdr.{AllowTrustOp, AssetCode12, AssetCode4, AssetType, Asset => XAsset}
7+
import org.stellar.xdr.{AllowTrustOp, AssetCode, AssetCode12, AssetCode4, AssetType, Asset => XAsset}
88
import stellar.sdk.util.ByteArrays._
99
import stellar.sdk.{KeyPair, PublicKeyOps}
1010

@@ -16,7 +16,7 @@ sealed trait Asset {
1616
}
1717

1818
object Asset {
19-
def decodeCode(asset: AllowTrustOp.AllowTrustOpAsset): String = {
19+
def decodeCode(asset: AssetCode): String = {
2020
asset.getDiscriminant match {
2121
case AssetType.ASSET_TYPE_CREDIT_ALPHANUM4 =>
2222
paddedByteArrayToString(asset.getAssetCode4.getAssetCode4)

src/main/scala/stellar/sdk/model/StrKey.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package stellar.sdk.model
22

33
import java.nio.ByteBuffer
4-
54
import org.apache.commons.codec.binary.Base32
65
import org.stellar.xdr.{AccountID, CryptoKeyType, MuxedAccount, PublicKeyType, SignerKey, SignerKeyType, Uint256, Uint64, PublicKey => XPublicKey}
76
import stellar.sdk.model.StrKey.codec
87
import stellar.sdk.util.ByteArrays
98
import stellar.sdk.{KeyPair, PublicKey}
109

10+
import scala.util.Try
11+
1112

1213
/**
1314
* A StrKey (Stellar Key) is a typed, encoded byte array.

src/main/scala/stellar/sdk/model/ledger/LedgerEntry.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ object AccountEntry {
170170
lx,
171171
ext2.getV2.getNumSponsored.getUint32.toInt,
172172
ext2.getV2.getNumSponsoring.getUint32.toInt,
173-
ext2.getV2.getSignerSponsoringIDs.map(_.getSponsorshipDescriptor).map(AccountId.decodeXdr).toList
173+
ext2.getV2.getSignerSponsoringIDs
174+
.map(_.getSponsorshipDescriptor)
175+
.filter(_ != null)
176+
.map(AccountId.decodeXdr).toList
174177
)
175178
}
176179

0 commit comments

Comments
 (0)