diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlock.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlock.java
index a242852e920a180971693a9c7127503f47e11d5e..a4104b3667a6de8b8dfa42eda4c50a7b06277eda 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlock.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlock.java
@@ -482,9 +482,17 @@ public class BlockchainBlock implements Serializable {
 
 
     public static class Revoked implements Serializable {
+        private String pubkey;
         private String signature;
-        private String userId;
 
+
+        public String getPubkey() {
+            return pubkey;
+        }
+
+        public void setPubkey(String pubkey) {
+            this.pubkey = pubkey;
+        }
         public String getSignature() {
             return signature;
         }
@@ -492,20 +500,12 @@ public class BlockchainBlock implements Serializable {
             this.signature = signature;
         }
 
-        public String getUserId() {
-            return userId;
-        }
-
-        public void setUserId(String userId) {
-            this.userId = userId;
-        }
-
         @Override
         public String toString() {
 
             StringBuilder sb = new StringBuilder()
-                    .append(signature)
-                    .append(":").append(userId);
+                    .append(pubkey)
+                    .append(":").append(signature);
 
             return sb.toString();
         }
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlocks.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlocks.java
index 05040f22c3d3ebcf9fe70265c52de8ea2142c08c..1aace4642ac6e3eef446f713b43e7ad3f507e248 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlocks.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlocks.java
@@ -22,9 +22,7 @@ package org.duniter.core.client.model.bma;
  * #L%
  */
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import org.duniter.core.util.Preconditions;
 
 import java.math.BigInteger;
 import java.util.*;
@@ -42,7 +40,10 @@ public final class BlockchainBlocks {
 
     public static final Pattern SIG_PUBKEY_PATTERN = Pattern.compile("SIG\\(([^)]+)\\)");
 
-    public static final Pattern TX_UNLOCK_PATTERN = Pattern.compile("([0-9]+):SIG\\(([^)]+)\\)");
+    public static final Pattern TX_INPUT_CONDITION_FUNCTION = Pattern.compile("(SIG|XHX)\\(([^)]+)\\)");
+    public static final Pattern TX_INPUT_CONDITION = Pattern.compile(TX_INPUT_CONDITION_FUNCTION + "(:? " + TX_INPUT_CONDITION_FUNCTION + ")*");
+
+    public static final Pattern TX_UNLOCK_PATTERN = Pattern.compile("([0-9]+):(" + TX_INPUT_CONDITION+")");
     public static final Pattern TX_OUTPUT_PATTERN = Pattern.compile("([0-9]+):([0-9]+):([^:]+)");
     public static final Pattern TX_INPUT_PATTERN = Pattern.compile("([0-9]+):([0-9]+):([TD]):([^:]+):([^:]+)");
 
@@ -64,28 +65,25 @@ public final class BlockchainBlocks {
     public static long getTxAmount(final BlockchainBlock.Transaction tx,
                                    Predicate<String> issuerFilter) {
 
-        final Map<Integer, Integer> inputIndexByIssuerIndex = Maps.newHashMap();
-        Arrays.stream(tx.getUnlocks())
-                .map(TX_UNLOCK_PATTERN::matcher)
-                .filter(Matcher::matches)
-                .forEach(matcher -> inputIndexByIssuerIndex.put(
-                        Integer.parseInt(matcher.group(1)),
-                        Integer.parseInt(matcher.group(2)))
-                );
+        final Map<Integer, List<String>> inputIssuers = getInputIssuers(tx);
 
         return IntStream.range(0, tx.getIssuers().length)
-                .mapToLong(i -> {
-                    final String issuer = tx.getIssuers()[i];
+                .mapToLong(issuerIndex -> {
+                    final String issuer = tx.getIssuers()[issuerIndex];
 
                     // Skip if issuerFilter test failed
                     if (issuerFilter != null && !issuerFilter.test(issuer)) return 0;
 
                     long inputSum = IntStream.range(0, tx.getInputs().length)
-                            .filter(j -> i == inputIndexByIssuerIndex.get(j))
-                            .mapToObj(j -> tx.getInputs()[j])
-                            .map(input -> input.split(":"))
-                            .filter(inputParts -> inputParts.length > 2)
-                            .mapToLong(inputParts -> powBase(Long.parseLong(inputParts[0]), Integer.parseInt(inputParts[1])))
+                            .filter(inputIssuers::containsKey)
+                            .mapToLong(inputIndex -> {
+                                String[] inputParts = tx.getInputs()[inputIndex].split(":");
+                                List<String> issuers = inputIssuers.get(inputIndex);
+                                if (inputParts.length > 2 && issuers.contains(issuer)) {
+                                    return powBase(Long.parseLong(inputParts[0]), Integer.parseInt(inputParts[1]), issuers.size());
+                                }
+                                return 0;
+                            })
                             .sum();
 
                     long outputSum = Arrays.stream(tx.getOutputs())
@@ -105,15 +103,20 @@ public final class BlockchainBlocks {
         return amount * (long)Math.pow(10, unitbase);
     }
 
+    public static long powBase(long amount, int unitbase, int divisor) {
+        if (unitbase == 0) return amount;
+        return amount * (long)Math.pow(10, unitbase) / divisor;
+    }
+
     public static List<TxInput> getTxInputs(final BlockchainBlock.Transaction tx) {
         Preconditions.checkNotNull(tx);
 
-        final Function<Integer, String> issuerByInputIndex = transformInputIndex2Issuer(tx);
+        final Map<Integer, List<String>> inputIssuers = getInputIssuers(tx);
 
         return IntStream.range(0, tx.getInputs().length)
                 .mapToObj(i -> {
                     TxInput txInput = parseInput(tx.getInputs()[i]);
-                    txInput.issuer = issuerByInputIndex.apply(i);
+                    txInput.issuers = inputIssuers.get(i);
                     return txInput;
                 })
                 .collect(Collectors.toList());
@@ -162,8 +165,8 @@ public final class BlockchainBlocks {
         Preconditions.checkNotNull(txInputs);
         return txInputs.stream()
                 // only keep inputs from issuer
-                .filter(input -> Objects.equals(issuer, input.issuer))
-                .mapToLong(input -> powBase(input.amount, input.unitbase))
+                .filter(input -> input.issuers.contains(issuer))
+                .mapToLong(input -> powBase(input.amount, input.unitbase, input.issuers.size()))
                 .sum();
     }
 
@@ -180,7 +183,9 @@ public final class BlockchainBlocks {
 
     public static Set<String> getTxRecipients(Collection<TxOutput> txOutputs) {
         Preconditions.checkNotNull(txOutputs);
-        return txOutputs.stream().map(output -> output.recipient).distinct().collect(Collectors.toSet());
+        return txOutputs.stream().map(output -> output.recipient)
+                .filter(Objects::nonNull)
+                .distinct().collect(Collectors.toSet());
     }
 
     public static class TxInput {
@@ -189,7 +194,7 @@ public final class BlockchainBlocks {
         public String type;
         public String txHashOrPubkey;
         public String indexOrBlockId;
-        public String issuer;
+        public List<String> issuers;
 
         public boolean isUD() {
             return "D".equals(type);
@@ -206,18 +211,30 @@ public final class BlockchainBlocks {
     /* -- Internal methods -- */
 
 
-    private static Function<Integer, String> transformInputIndex2Issuer(final BlockchainBlock.Transaction tx) {
-        final Map<Integer, Integer> inputIndexByIssuerIndex = Maps.newHashMap();
-        Arrays.stream(tx.getUnlocks())
+    private static Map<Integer, List<String>> getInputIssuers(final BlockchainBlock.Transaction tx) {
+        return Arrays.stream(tx.getUnlocks())
                 .map(TX_UNLOCK_PATTERN::matcher)
                 .filter(Matcher::matches)
-                .forEach(matcher -> inputIndexByIssuerIndex.put(
-                        Integer.parseInt(matcher.group(1)),
-                        Integer.parseInt(matcher.group(2)))
+                .collect(Collectors.toMap(
+                        matcher -> Integer.decode(matcher.group(1)),
+                        matcher -> getUnlockConditionIssuers(tx.getIssuers(), matcher.group(2)))
                 );
-
-
-        return (inputIndex -> tx.getIssuers()[inputIndexByIssuerIndex.get(inputIndex)]);
     }
 
+    private static List<String> getUnlockConditionIssuers(String[] issuers, String condition) {
+        // parse condition
+        Matcher matcher = TX_INPUT_CONDITION_FUNCTION.matcher(condition);
+        int start = 0;
+        List<String> result = new ArrayList<>(1);
+        while (matcher.find(start)) {
+            String function = matcher.group(1);
+            if ("SIG".equals(function)) {
+                int issuerIndex = Integer.parseInt(matcher.group(2));
+                String issuer = issuers[issuerIndex];
+                result.add(issuer);
+            }
+            start = matcher.end();
+        }
+        return result;
+    }
 }
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/RevokedDeserializer.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/RevokedDeserializer.java
index 25d418a858b973319185dbdfb7570ef70199a1de..9d25954a3228d245ec086327911648c255d246ee 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/RevokedDeserializer.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/RevokedDeserializer.java
@@ -50,8 +50,8 @@ public class RevokedDeserializer extends JsonDeserializer<BlockchainBlock.Revoke
         BlockchainBlock.Revoked result = new BlockchainBlock.Revoked();
         int i = 0;
 
+        result.setPubkey(parts[i++]);
         result.setSignature(parts[i++]);
-        result.setUserId(parts[i++]);
 
         return result;
     }
diff --git a/duniter4j-core-shared/src/main/java/org/duniter/core/util/CollectionUtils.java b/duniter4j-core-shared/src/main/java/org/duniter/core/util/CollectionUtils.java
index f686b38b7fd876bc5f923875d11e642ec99b1bd2..995e10b8210e033d3804efa6cc9b00ee1ad78e12 100644
--- a/duniter4j-core-shared/src/main/java/org/duniter/core/util/CollectionUtils.java
+++ b/duniter4j-core-shared/src/main/java/org/duniter/core/util/CollectionUtils.java
@@ -84,6 +84,10 @@ public class CollectionUtils {
         return collection == null ? 0 : collection.size();
     }
 
+    public static int size(final Object[] array) {
+        return array == null ? 0 : array.length;
+    }
+
     public static <E> E extractSingleton(Collection<E> collection) {
         if(collection != null && collection.size() == 1) {
             return collection.iterator().next();
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/BlockStatDaoImpl.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/BlockStatDaoImpl.java
index e4b8e7b3a4420b817be38bd57f5767d06e94993c..f79a6527795bfb483977b111d62509e70087562a 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/BlockStatDaoImpl.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/BlockStatDaoImpl.java
@@ -252,11 +252,16 @@ public class BlockStatDaoImpl extends AbstractDao implements BlockStatDao {
                     .field("type", "long")
                     .endObject()
 
-                    // txChangeAmount
+                    // txChangeCount
                     .startObject(BlockchainBlockStat.PROPERTY_TX_CHANGE_COUNT)
                     .field("type", "integer")
                     .endObject()
 
+                    // certCount
+                    .startObject(BlockchainBlockStat.PROPERTY_CERT_COUNT)
+                    .field("type", "integer")
+                    .endObject()
+
                     .endObject()
                     .endObject().endObject();
 
@@ -295,6 +300,9 @@ public class BlockStatDaoImpl extends AbstractDao implements BlockStatDao {
             result.setTxCount(0);
         }
 
+        // Cert count
+        result.setCertCount(CollectionUtils.size(block.getCertifications()));
+
         return result;
     }
 
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/model/BlockchainBlockStat.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/model/BlockchainBlockStat.java
index 94c6a922a07a0b65c5e2d42a102b8231f0a3e21e..add32d5ca052cd277827c7470721fa9c39385155 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/model/BlockchainBlockStat.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/model/BlockchainBlockStat.java
@@ -43,6 +43,7 @@ public class BlockchainBlockStat implements Serializable {
     public static final String PROPERTY_TX_COUNT = "txCount";
     public static final String PROPERTY_TX_AMOUNT = "txAmount";
     public static final String PROPERTY_TX_CHANGE_COUNT = "txChangeCount";
+    public static final String PROPERTY_CERT_COUNT = "certCount";
 
     // Property copied from Block
     private int version;
@@ -60,6 +61,7 @@ public class BlockchainBlockStat implements Serializable {
     private Integer txCount;
     private BigInteger txAmount;
     private Integer txChangeCount;
+    private Integer certCount;
 
     public BlockchainBlockStat() {
         super();
@@ -169,4 +171,11 @@ public class BlockchainBlockStat implements Serializable {
         this.unitbase = unitbase;
     }
 
+    public Integer getCertCount() {
+        return certCount;
+    }
+
+    public void setCertCount(Integer certCount) {
+        this.certCount = certCount;
+    }
 }
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java
index db84a9a7cf1125f1047e0ff32e7542f47af8e7dc..f56e1474ff3219c7d20e9c7870bc77965aec4f0b 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java
@@ -35,6 +35,8 @@ public enum UserEventCodes {
     MEMBER_JOIN,
     MEMBER_LEAVE,
     MEMBER_ACTIVE,
+    MEMBER_REVOKE,
+    MEMBER_EXCLUDE,
 
     // TX
     TX_SENT,
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java
index 738864c7e1b3578ca873bb53c13c132dc806d27a..626fbbbde0f9ee5865c68d8ac1e6e262ac726e4e 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java
@@ -95,6 +95,13 @@ public class BlockchainUserEventService extends AbstractBlockchainListenerServic
             }
         }
 
+        // Actives
+        if (CollectionUtils.isNotEmpty(block.getActives())) {
+            for (BlockchainBlock.Joiner active: block.getActives()) {
+                notifyUserEvent(block, active.getPublicKey(), UserEventCodes.MEMBER_ACTIVE, I18n.n("duniter.user.event.MEMBER_ACTIVE"), block.getCurrency());
+            }
+        }
+
         // Leavers
         if (CollectionUtils.isNotEmpty(block.getLeavers())) {
             for (BlockchainBlock.Joiner leaver: block.getJoiners()) {
@@ -102,10 +109,17 @@ public class BlockchainUserEventService extends AbstractBlockchainListenerServic
             }
         }
 
-        // Actives
-        if (CollectionUtils.isNotEmpty(block.getActives())) {
-            for (BlockchainBlock.Joiner active: block.getActives()) {
-                notifyUserEvent(block, active.getPublicKey(), UserEventCodes.MEMBER_ACTIVE, I18n.n("duniter.user.event.MEMBER_ACTIVE"), block.getCurrency());
+        // Revoked
+        if (CollectionUtils.isNotEmpty(block.getRevoked())) {
+            for (BlockchainBlock.Revoked revoked: block.getRevoked()) {
+                notifyUserEvent(block, revoked.getPubkey(), UserEventCodes.MEMBER_REVOKE, I18n.n("duniter.user.event.MEMBER_REVOKE"), block.getCurrency());
+            }
+        }
+
+        // Excluded
+        if (CollectionUtils.isNotEmpty(block.getExcluded())) {
+            for (String excluded: block.getExcluded()) {
+                notifyUserEvent(block, excluded, UserEventCodes.MEMBER_EXCLUDE, I18n.n("duniter.user.event.MEMBER_EXCLUDE"), block.getCurrency());
             }
         }
 
diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties
index 7110fb3bdd703cea3d7c23989bfb0313a29920b6..98d218d1ac2674b2a99cc8e0d673c1eb85d6576e 100644
--- a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties
+++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties
@@ -5,8 +5,10 @@ duniter.user.event.CERT_RECEIVED=You have received a certification from %2$s.
 duniter.user.event.CERT_SENT=Your certification to %2$s was executed.
 duniter.user.event.INVITATION_TO_CERTIFY=%2$s invites you to certify an identity.
 duniter.user.event.MEMBER_ACTIVE=Your membership to %1$s has been renewed successfully.
+duniter.user.event.MEMBER_EXCLUDE=
 duniter.user.event.MEMBER_JOIN=You are now a member of currency %1$s\!
 duniter.user.event.MEMBER_LEAVE=You are not a member anymore of currency %1$s\!
+duniter.user.event.MEMBER_REVOKE=
 duniter.user.event.MESSAGE_RECEIVED=You received a message from %2$s.
 duniter.user.event.NODE_BMA_DOWN=Duniter node [%1$s\:%2$s] is DOWN\: no access from ES node [%3$s]. Last connexion at %4$s. Blockchain indexation waiting.
 duniter.user.event.NODE_BMA_UP=Duniter node [%1$s\:%2$s] is UP again.
diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties
index be2471dfe68a8b57fe958b79b6410933fbe396e2..e4e31362232b4af585877a5b90a3541bf51c8da1 100644
--- a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties
+++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties
@@ -5,8 +5,10 @@ duniter.user.event.CERT_RECEIVED=%2$s vous a certifié (certification prise en c
 duniter.user.event.CERT_SENT=Votre certification de %2$s a été pris en compte.
 duniter.user.event.INVITATION_TO_CERTIFY=%2$s vous invite à certifier une identité.
 duniter.user.event.MEMBER_ACTIVE=Votre adhésion comme membre a bien été renouvellée.
+duniter.user.event.MEMBER_EXCLUDE=Votre adhésion comme membre n'est plus valide, faute de non renouvellement ou par manque de certifications.
 duniter.user.event.MEMBER_JOIN=Vous êtes maintenant membre de la monnaie %1$s \!
-duniter.user.event.MEMBER_LEAVE=Votre adhésion comme membre à expirée.
+duniter.user.event.MEMBER_LEAVE=Votre adhésion a pris fin, suite à votre demande.
+duniter.user.event.MEMBER_REVOKE=Votre compte membre a été révoquée. Il ne pourra plus devenir membre.
 duniter.user.event.MESSAGE_RECEIVED=Vous avez reçu un message de %2$s.
 duniter.user.event.NODE_BMA_DOWN=Noeud Duniter [%1$s\:%2$s] non joignable, depuis le noeud ES API [%3$s]. Dernière connexion à %4$s. Indexation de blockchain en attente.
 duniter.user.event.NODE_BMA_UP=Noeud Duniter [%1$s\:%2$s] à nouveau accessible.